MASSA-Algorithm 1.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- MASSA_Algorithm/MASSA.py +63 -27
- MASSA_Algorithm/MASSAargs.py +19 -4
- MASSA_Algorithm/MASSAcluster.py +177 -15
- MASSA_Algorithm/MASSAextraction.py +36 -5
- MASSA_Algorithm/MASSAlogos.py +5 -1
- MASSA_Algorithm/MASSAmoloutput.py +23 -4
- MASSA_Algorithm/MASSAod.py +7 -6
- MASSA_Algorithm/MASSAopen_files.py +15 -16
- MASSA_Algorithm/MASSApreparation.py +1 -4
- MASSA_Algorithm/MASSAsplit.py +2 -0
- MASSA_Algorithm/pyMASSA.py +353 -0
- {massa_algorithm-1.0.0.dist-info → massa_algorithm-2.0.1.dist-info}/METADATA +19 -4
- massa_algorithm-2.0.1.dist-info/RECORD +19 -0
- massa_algorithm-1.0.0.dist-info/RECORD +0 -18
- {massa_algorithm-1.0.0.dist-info → massa_algorithm-2.0.1.dist-info}/WHEEL +0 -0
- {massa_algorithm-1.0.0.dist-info → massa_algorithm-2.0.1.dist-info}/entry_points.txt +0 -0
- {massa_algorithm-1.0.0.dist-info → massa_algorithm-2.0.1.dist-info}/licenses/LICENSE +0 -0
- {massa_algorithm-1.0.0.dist-info → massa_algorithm-2.0.1.dist-info}/top_level.txt +0 -0
MASSA_Algorithm/MASSA.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import time
|
|
1
2
|
from MASSA_Algorithm import MASSAlogos
|
|
2
3
|
from MASSA_Algorithm import MASSAargs
|
|
3
4
|
from MASSA_Algorithm import MASSAod
|
|
@@ -8,6 +9,8 @@ from MASSA_Algorithm import MASSApreparation
|
|
|
8
9
|
from MASSA_Algorithm import MASSAcluster
|
|
9
10
|
from MASSA_Algorithm import MASSAsplit
|
|
10
11
|
from MASSA_Algorithm import MASSAmoloutput
|
|
12
|
+
from joblib import parallel_backend
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
def returns_zero(total, test, validation):
|
|
@@ -48,10 +51,11 @@ def main():
|
|
|
48
51
|
Main subroutine, allows the program to run directly from
|
|
49
52
|
the command line when installed via pip.
|
|
50
53
|
"""
|
|
54
|
+
parallel_backend('multiprocessing')
|
|
51
55
|
# Initializing from the command line.
|
|
52
56
|
# Print the program logo:
|
|
53
57
|
MASSAlogos.initial_print()
|
|
54
|
-
|
|
58
|
+
start_time = time.time()
|
|
55
59
|
# It captures command line arguments.
|
|
56
60
|
(file_input,
|
|
57
61
|
file_output,
|
|
@@ -67,18 +71,32 @@ def main():
|
|
|
67
71
|
linkage_method,
|
|
68
72
|
flag_dendrogram,
|
|
69
73
|
splitting_strategy,
|
|
70
|
-
drop_errors
|
|
74
|
+
drop_errors,
|
|
75
|
+
large_library) = MASSAargs.capture_args()
|
|
71
76
|
print('Initializing, wait...\n')
|
|
72
77
|
|
|
73
78
|
# Create log.txt file in directory:
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
log_path = directoryfile_output+'/log.txt'
|
|
80
|
+
write_log = open(log_path, 'w')
|
|
76
81
|
|
|
77
82
|
# Initial file management:
|
|
78
|
-
# It creates the output directories.
|
|
79
|
-
MASSAod.output_directory(directoryfile_output)
|
|
80
83
|
# Read molecules.
|
|
81
|
-
mols = MASSAopen_files.read_molecules(file_input,
|
|
84
|
+
mols = MASSAopen_files.read_molecules(file_input, write_log, drop_errors)
|
|
85
|
+
|
|
86
|
+
# If auto detection is enable to select the initial cluster algorithm.
|
|
87
|
+
if large_library == 'auto':
|
|
88
|
+
if int(len(mols)) > 9999:
|
|
89
|
+
large_library = True
|
|
90
|
+
else:
|
|
91
|
+
large_library = False
|
|
92
|
+
|
|
93
|
+
if large_library == True:
|
|
94
|
+
# Deactivates the HCA dendrogram.
|
|
95
|
+
flag_dendrogram = False
|
|
96
|
+
|
|
97
|
+
# It creates the output directories.
|
|
98
|
+
MASSAod.output_directory(directoryfile_output, flag_dendrogram)
|
|
99
|
+
|
|
82
100
|
# Extracting the property names from the ".sdf" input file.
|
|
83
101
|
sdf_property_names = MASSAopen_files.get_sdf_property_names(mols)
|
|
84
102
|
# Structure 3D management - It adds hydrogens keeping 3D coordenates.
|
|
@@ -87,6 +105,7 @@ def main():
|
|
|
87
105
|
# Extraction properties from ".sdf":
|
|
88
106
|
# It extracts the names of the molecules and creates a name:molecule dictionary and a dataframe.
|
|
89
107
|
names, dataframe = MASSAextraction.name_extraction(mols_h)
|
|
108
|
+
|
|
90
109
|
# It defines a list of what biological activities are being extracted.
|
|
91
110
|
biological_activity = MASSAextraction.the_biological_handler(
|
|
92
111
|
sdf_property_names, number_bioact, bioactivities_as_args)
|
|
@@ -112,16 +131,28 @@ def main():
|
|
|
112
131
|
# PCA for the structural domain.
|
|
113
132
|
fp_pca = MASSApreparation.pca_maker(fp_matrix, npcs, svd_parameter)
|
|
114
133
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
if large_library == True:
|
|
135
|
+
# First clustering (MiniBatch-KMeans):
|
|
136
|
+
# It performs KMeans clustering for the biological domain.
|
|
137
|
+
bio_hca = MASSAcluster.kmeans_clusters(
|
|
138
|
+
bio_pca, names, 'bio', directoryfile_output, extension_type)
|
|
139
|
+
# It performs KMeans clustering for the physicochemical domain.
|
|
140
|
+
phch_hca = MASSAcluster.kmeans_clusters(
|
|
141
|
+
phch_pca, names, 'PhCh', directoryfile_output, extension_type)
|
|
142
|
+
# It performs KMeans clustering for the structural domain.
|
|
143
|
+
fp_hca = MASSAcluster.kmeans_clusters(
|
|
144
|
+
fp_pca, names, 'FP', directoryfile_output, extension_type)
|
|
145
|
+
else:
|
|
146
|
+
# First clustering (HCA):
|
|
147
|
+
# It performs HCA clustering without generating the dendrogram for the biological domain.
|
|
148
|
+
leaves_cluster_bio, bio_hca, linkage_bio, cutoff_bio = MASSAcluster.hca_clusters(
|
|
149
|
+
bio_pca, names, 'bio', directoryfile_output, extension_type, linkage_method)
|
|
150
|
+
# It performs HCA clustering without generating the dendrogram for the physicochemical domain.
|
|
151
|
+
leaves_cluster_phch, phch_hca, linkage_phch, cutoff_phch = MASSAcluster.hca_clusters(
|
|
152
|
+
phch_pca, names, 'PhCh', directoryfile_output, extension_type, linkage_method)
|
|
153
|
+
# It performs HCA clustering without generating the dendrogram for the structural domain.
|
|
154
|
+
leaves_cluster_fp, fp_hca, linkage_fp, cutoff_fp = MASSAcluster.hca_clusters(
|
|
155
|
+
fp_pca, names, 'FP', directoryfile_output, extension_type, linkage_method)
|
|
125
156
|
|
|
126
157
|
# It adds the biological cluster identification to the spreadsheet.
|
|
127
158
|
dataframe = MASSApreparation.organize_df_clusterization(
|
|
@@ -137,7 +168,8 @@ def main():
|
|
|
137
168
|
# It creates a matrix with cluster identifications for each of the three domains for Kmodes.
|
|
138
169
|
matrix_for_kmodes = MASSApreparation.organize_for_kmodes(dataframe)
|
|
139
170
|
# It performs Kmodes clustering for the general domain.
|
|
140
|
-
all_hca = MASSAcluster.kmodes_clusters(matrix_for_kmodes, names
|
|
171
|
+
all_hca = MASSAcluster.kmodes_clusters(matrix_for_kmodes, names,
|
|
172
|
+
directoryfile_output, extension_type)
|
|
141
173
|
# It adds the general cluster identification to the spreadsheet.
|
|
142
174
|
dataframe = MASSApreparation.organize_df_clusterization(
|
|
143
175
|
dataframe, all_hca, 'all')
|
|
@@ -239,27 +271,30 @@ def main():
|
|
|
239
271
|
MASSAsplit.log_of_distributions(
|
|
240
272
|
bio_distr, bio_total, bio_training,
|
|
241
273
|
bio_test, bio_val, splitting_strategy,
|
|
242
|
-
|
|
274
|
+
write_log
|
|
243
275
|
)
|
|
244
276
|
phch_distr = 'Physicochemical Distribution'
|
|
245
277
|
MASSAsplit.log_of_distributions(
|
|
246
278
|
phch_distr, phch_total, phch_training,
|
|
247
279
|
phch_test, phch_val, splitting_strategy,
|
|
248
|
-
|
|
280
|
+
write_log
|
|
249
281
|
)
|
|
250
282
|
fp_distr = 'Structural (FP) Distribution'
|
|
251
283
|
MASSAsplit.log_of_distributions(
|
|
252
284
|
fp_distr, fp_total, fp_training,
|
|
253
285
|
fp_test, fp_val, splitting_strategy,
|
|
254
|
-
|
|
286
|
+
write_log
|
|
255
287
|
)
|
|
256
288
|
all_distr = 'General Distribution'
|
|
257
289
|
MASSAsplit.log_of_distributions(
|
|
258
290
|
all_distr, all_total, all_training,
|
|
259
291
|
all_test, all_val, splitting_strategy,
|
|
260
|
-
|
|
292
|
+
write_log
|
|
261
293
|
)
|
|
262
|
-
|
|
294
|
+
|
|
295
|
+
# Output management:
|
|
296
|
+
# It adds, for each molecule, the values of the calculated properties, the identifications of each cluster and which set the molecule belongs to.
|
|
297
|
+
MASSAmoloutput.output_file_mols(dataframe, file_output)
|
|
263
298
|
|
|
264
299
|
# Plot HCAs:
|
|
265
300
|
if flag_dendrogram == True:
|
|
@@ -280,7 +315,8 @@ def main():
|
|
|
280
315
|
extension_type, dendrogram_xfont_size, test_or_val_molecules
|
|
281
316
|
) # fp_Plot: Plot the HCA dendrogram
|
|
282
317
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
318
|
+
end_time = time.time()
|
|
319
|
+
elapsed_time = end_time - start_time
|
|
320
|
+
write_log.write(f'Completed! Elapsed time = {elapsed_time} s.')
|
|
321
|
+
write_log.close()
|
|
322
|
+
print(f'Completed! Elapsed time = {elapsed_time} s.')
|
MASSA_Algorithm/MASSAargs.py
CHANGED
|
@@ -26,7 +26,7 @@ def capture_args():
|
|
|
26
26
|
'[-n <number_of_principal_components>] [-v <svd_parameter>] [-l <linkage_method] '
|
|
27
27
|
'[-t <extension_for_images>] [-d <dendrogram_font_size_on_Xaxis>] '
|
|
28
28
|
'[-x <frequency_bar_plot_font_size_on_Xaxis>] [-f <toggle_dendrogram>] '
|
|
29
|
-
'[-e <drop_molecules_with_errors]'
|
|
29
|
+
'[-e <drop_molecules_with_errors] [-a <large_dataset>]'
|
|
30
30
|
)
|
|
31
31
|
)
|
|
32
32
|
required = parser.add_argument_group('required arguments')
|
|
@@ -120,9 +120,17 @@ def capture_args():
|
|
|
120
120
|
)
|
|
121
121
|
)
|
|
122
122
|
optional.add_argument('-e', '--drop_errors', type=str, choices=['true', 'false'],
|
|
123
|
-
metavar='DROP_MOLECULES_WITH_ERRORS', default='
|
|
123
|
+
metavar='DROP_MOLECULES_WITH_ERRORS', default='true', help=(
|
|
124
124
|
'Ignore chemistry errors, saving only molecules without any errors. '
|
|
125
|
-
'
|
|
125
|
+
'False will treat molecule errors as fatal and fail the execution. '
|
|
126
|
+
'Default = \'true\'.'
|
|
127
|
+
))
|
|
128
|
+
optional.add_argument('-a', '--large_dataset', type=str, choices=['auto', 'true', 'false'],
|
|
129
|
+
metavar='LARGE_DATASET', default='auto', help=(
|
|
130
|
+
'Switch the MASSA algorithm from HCA to KMeans to handle large datasets. '
|
|
131
|
+
'Options = auto (switches to KMeans if ≥ 10,000 molecules), '
|
|
132
|
+
'false = use HCA (for smaller datasets), '
|
|
133
|
+
'true = use KMeans (for large datasets).'
|
|
126
134
|
))
|
|
127
135
|
args = parser.parse_args()
|
|
128
136
|
|
|
@@ -153,6 +161,12 @@ def capture_args():
|
|
|
153
161
|
flag_strategy = str(args.splitting_strategy).lower()
|
|
154
162
|
# It captures whether or not to ignore chemistry errors.
|
|
155
163
|
drop = str(args.drop_errors).lower()
|
|
164
|
+
# It captures whether or not is handling with a large dataset.
|
|
165
|
+
large_dataset = str(args.large_dataset).lower()
|
|
166
|
+
if large_dataset == 'true':
|
|
167
|
+
large_dataset = True
|
|
168
|
+
elif large_dataset == 'false':
|
|
169
|
+
large_dataset = False
|
|
156
170
|
|
|
157
171
|
if args.the_biological_activities != None:
|
|
158
172
|
# It creates a list with the names of the biological activities to be searched for.
|
|
@@ -210,5 +224,6 @@ def capture_args():
|
|
|
210
224
|
linkage_method,
|
|
211
225
|
flag_dendrogram,
|
|
212
226
|
flag_strategy,
|
|
213
|
-
drop
|
|
227
|
+
drop,
|
|
228
|
+
large_dataset
|
|
214
229
|
)
|
MASSA_Algorithm/MASSAcluster.py
CHANGED
|
@@ -3,6 +3,9 @@ from matplotlib import pyplot as plt
|
|
|
3
3
|
from matplotlib import colors as colrs
|
|
4
4
|
import scipy.cluster.hierarchy as sch
|
|
5
5
|
from kmodes import kmodes
|
|
6
|
+
from sklearn.cluster import MiniBatchKMeans as KMeans
|
|
7
|
+
from multiprocessing import cpu_count as mpi_count
|
|
8
|
+
from scipy.spatial.distance import cdist
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
def numerical_number_of_clusters(distances_list):
|
|
@@ -26,12 +29,15 @@ def numerical_number_of_clusters(distances_list):
|
|
|
26
29
|
return distances.index(max(distances)) + 1
|
|
27
30
|
|
|
28
31
|
|
|
29
|
-
def categorical_number_of_clusters(file):
|
|
32
|
+
def categorical_number_of_clusters(file, directory_fileoutput, extension_type):
|
|
30
33
|
# Elbow method using the clustering cost:
|
|
31
34
|
list_clus_costs = []
|
|
32
|
-
|
|
35
|
+
maxcluster = 31
|
|
36
|
+
ran_curve = [x for x in range(2, maxcluster, 2)]
|
|
37
|
+
for n in range(2, maxcluster): # Calculate the cost for each n cluster.
|
|
33
38
|
km = kmodes.KModes(n_clusters=n, verbose=0,
|
|
34
|
-
n_init=20, init='Cao',
|
|
39
|
+
n_init=20, init='Cao',
|
|
40
|
+
n_jobs=-1, max_iter=300)
|
|
35
41
|
km.fit(X=file)
|
|
36
42
|
# Costs = Clustering cost, defined as the sum distance of all points
|
|
37
43
|
# to their respective cluster centroids.
|
|
@@ -39,10 +45,10 @@ def categorical_number_of_clusters(file):
|
|
|
39
45
|
# Define the x-axis coordinates of the first and last points on the line.
|
|
40
46
|
x1, y1 = 2, list_clus_costs[0]
|
|
41
47
|
# Define the y-axis coordinates of the first and last points on the line.
|
|
42
|
-
x2, y2 = 20, list_clus_costs[
|
|
48
|
+
x2, y2 = 20, list_clus_costs[18]
|
|
43
49
|
|
|
44
50
|
distances = []
|
|
45
|
-
for i in range(
|
|
51
|
+
for i in range(19):
|
|
46
52
|
# Coordinate on the x-axis of the point to calculate the distance to the line.
|
|
47
53
|
x0 = i+2
|
|
48
54
|
# Coordinate on the y-axis of the point to calculate the distance to the line.
|
|
@@ -51,7 +57,52 @@ def categorical_number_of_clusters(file):
|
|
|
51
57
|
denominator = ((y2 - y1)**2 + (x2 - x1)**2)**(1/2)
|
|
52
58
|
# Distance between a point and a line.
|
|
53
59
|
distances.append(numerator/denominator)
|
|
54
|
-
|
|
60
|
+
number_of_clusters = (distances.index(max(distances)) + 2)
|
|
61
|
+
ident = "all"
|
|
62
|
+
plot_distortion(
|
|
63
|
+
list_clus_costs, directory_fileoutput,
|
|
64
|
+
ident, extension_type, number_of_clusters,
|
|
65
|
+
ran_curve)
|
|
66
|
+
return number_of_clusters
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def kmeans_number_of_clusters(file, ident, directory_fileoutput, extension_type):
|
|
70
|
+
# Elbow method using the clustering cost:
|
|
71
|
+
list_clus_costs = []
|
|
72
|
+
maxcluster = 31
|
|
73
|
+
ran_curve = [x for x in range(2, maxcluster, 2)]
|
|
74
|
+
for n in range(2, maxcluster): # Calculate the cost for each n cluster.
|
|
75
|
+
km = KMeans(n_clusters=n, verbose=0,
|
|
76
|
+
init='k-means++', max_iter=300,
|
|
77
|
+
batch_size=256*int(mpi_count()),
|
|
78
|
+
compute_labels=True, random_state=2025)
|
|
79
|
+
km.fit(X=file)
|
|
80
|
+
# Costs = Clustering cost, defined as the sum distance of all points
|
|
81
|
+
# to their respective cluster centroids.
|
|
82
|
+
list_clus_costs.append(
|
|
83
|
+
np.sum(np.min(cdist(file, km.cluster_centers_, 'euclidean'), axis=1)**2) / file.shape[0])
|
|
84
|
+
# Define the x-axis coordinates of the first and last points on the line.
|
|
85
|
+
x1, y1 = 2, list_clus_costs[0]
|
|
86
|
+
# Define the y-axis coordinates of the first and last points on the line.
|
|
87
|
+
x2, y2 = 20, list_clus_costs[18]
|
|
88
|
+
|
|
89
|
+
distances = []
|
|
90
|
+
for i in range(19):
|
|
91
|
+
# Coordinate on the x-axis of the point to calculate the distance to the line.
|
|
92
|
+
x0 = i+2
|
|
93
|
+
# Coordinate on the y-axis of the point to calculate the distance to the line.
|
|
94
|
+
y0 = list_clus_costs[i]
|
|
95
|
+
numerator = abs((y2-y1)*x0 - (x2-x1)*y0 + x2*y1 - y2*x1)
|
|
96
|
+
denominator = ((y2 - y1)**2 + (x2 - x1)**2)**(1/2)
|
|
97
|
+
# Distance between a point and a line.
|
|
98
|
+
distances.append(numerator/denominator)
|
|
99
|
+
|
|
100
|
+
number_of_clusters = (distances.index(max(distances)) + 2)
|
|
101
|
+
plot_distortion(
|
|
102
|
+
list_clus_costs, directory_fileoutput,
|
|
103
|
+
ident, extension_type, number_of_clusters,
|
|
104
|
+
ran_curve)
|
|
105
|
+
return number_of_clusters
|
|
55
106
|
|
|
56
107
|
|
|
57
108
|
def optimal_threshold(file, ident, directory_fileoutput, extension_type, linkage):
|
|
@@ -69,7 +120,7 @@ def optimal_threshold(file, ident, directory_fileoutput, extension_type, linkage
|
|
|
69
120
|
if ident == 'bio':
|
|
70
121
|
# Define the name, directory and extension of the Euclidean distance graph.
|
|
71
122
|
name_euc_dist_file = directory_fileoutput + \
|
|
72
|
-
'
|
|
123
|
+
'Images/Distance_Images/Dist_HCA_biological.'+extension_type
|
|
73
124
|
title_distances = 'Plot of Euclidean Distances from Biological HCA'
|
|
74
125
|
print('Number of clusters:')
|
|
75
126
|
# Print the number of clusters for the biological domain.
|
|
@@ -80,7 +131,7 @@ def optimal_threshold(file, ident, directory_fileoutput, extension_type, linkage
|
|
|
80
131
|
elif ident == 'PhCh':
|
|
81
132
|
# Define the name, directory and extension of the Euclidean distance graph.
|
|
82
133
|
name_euc_dist_file = directory_fileoutput + \
|
|
83
|
-
'
|
|
134
|
+
'Images/Distance_Images/Dist_HCA_physicochemical.'+extension_type
|
|
84
135
|
title_distances = 'Plot of Euclidean Distances from Physicochemical HCA'
|
|
85
136
|
# Print the number of clusters for the physicochemical domain.
|
|
86
137
|
print('Physicochemical:', number_of_clusters)
|
|
@@ -90,7 +141,7 @@ def optimal_threshold(file, ident, directory_fileoutput, extension_type, linkage
|
|
|
90
141
|
else:
|
|
91
142
|
# Define the name, directory and extension of the Euclidean distance graph.
|
|
92
143
|
name_euc_dist_file = directory_fileoutput + \
|
|
93
|
-
'
|
|
144
|
+
'Images/Distance_Images/Dist_HCA_structural.'+extension_type
|
|
94
145
|
title_distances = 'Plot of Euclidean Distances from Structural HCA'
|
|
95
146
|
# Print the number of clusters for the structural domain.
|
|
96
147
|
print('Structural:', number_of_clusters)
|
|
@@ -279,24 +330,24 @@ def hca_plot(
|
|
|
279
330
|
if ident == 'bio':
|
|
280
331
|
plt.title('HCA of biological activity')
|
|
281
332
|
plt.savefig(directory_fileoutput +
|
|
282
|
-
'
|
|
333
|
+
'Images/Dendrogram_HCA_Images/HCA_biological.'+extension_type)
|
|
283
334
|
plt.close()
|
|
284
335
|
elif ident == 'PhCh':
|
|
285
336
|
plt.title('HCA of physicochemical properties')
|
|
286
337
|
plt.savefig(directory_fileoutput +
|
|
287
|
-
'
|
|
338
|
+
'Images/Dendrogram_HCA_Images/HCA_physicochemical.'+extension_type)
|
|
288
339
|
plt.close()
|
|
289
340
|
else:
|
|
290
341
|
plt.title('HCA of AtomPairs fingerprint')
|
|
291
342
|
plt.savefig(directory_fileoutput +
|
|
292
|
-
'
|
|
343
|
+
'Images/Dendrogram_HCA_Images/HCA_structural.'+extension_type)
|
|
293
344
|
plt.close()
|
|
294
345
|
|
|
295
346
|
|
|
296
|
-
def kmodes_clusters(file, names):
|
|
347
|
+
def kmodes_clusters(file, names, directory_fileoutput, extension_type):
|
|
297
348
|
# Calculate the number of clusters for the Kmodes:
|
|
298
|
-
n_clusters = categorical_number_of_clusters(
|
|
299
|
-
|
|
349
|
+
n_clusters = categorical_number_of_clusters(
|
|
350
|
+
file, directory_fileoutput, extension_type)
|
|
300
351
|
|
|
301
352
|
# Run Kmodes with the calculated number of clusters:
|
|
302
353
|
# Parameter setting.
|
|
@@ -311,3 +362,114 @@ def kmodes_clusters(file, names):
|
|
|
311
362
|
for i, a in zip(names, clusters_list):
|
|
312
363
|
HCA_General_dict[i] = int(a+1)
|
|
313
364
|
return HCA_General_dict
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def plot_distortion(
|
|
368
|
+
list_clus_costs, directory_fileoutput,
|
|
369
|
+
ident, extension_type, number_of_clusters,
|
|
370
|
+
ran_curve):
|
|
371
|
+
plt.plot(
|
|
372
|
+
list(range(2, len(list_clus_costs)+2)),
|
|
373
|
+
list_clus_costs, '-o',
|
|
374
|
+
color='black',
|
|
375
|
+
markersize=4, linewidth=1,
|
|
376
|
+
markeredgewidth=1
|
|
377
|
+
) # Initial setting of graph values and parameters.
|
|
378
|
+
if ident == 'bio':
|
|
379
|
+
# Define the name, directory and extension of the Distortion graph.
|
|
380
|
+
name_euc_dist_file = directory_fileoutput + \
|
|
381
|
+
'Images/Distance_Images/Distortion_biological.'+extension_type
|
|
382
|
+
title_distances = 'Plot of Distortion from Biological Space'
|
|
383
|
+
print('Number of clusters:')
|
|
384
|
+
# Print the number of clusters for the biological domain.
|
|
385
|
+
print('Biological:', number_of_clusters)
|
|
386
|
+
plt.ylabel('KMeans Distortion') # Set the y-axis label.
|
|
387
|
+
elif ident == 'PhCh':
|
|
388
|
+
# Define the name, directory and extension of the Distortion graph.
|
|
389
|
+
name_euc_dist_file = directory_fileoutput + \
|
|
390
|
+
'Images/Distance_Images/Distortion_physicochemical.'+extension_type
|
|
391
|
+
title_distances = 'Plot of Distortion from Physicochemical Space'
|
|
392
|
+
# Print the number of clusters for the physicochemical domain.
|
|
393
|
+
print('Physicochemical:', number_of_clusters)
|
|
394
|
+
plt.ylabel('KMeans Distortion') # Set the y-axis label.
|
|
395
|
+
elif ident == 'FP':
|
|
396
|
+
# Define the name, directory and extension of the Distortion graph.
|
|
397
|
+
name_euc_dist_file = directory_fileoutput + \
|
|
398
|
+
'Images/Distance_Images/Distortion_structural.'+extension_type
|
|
399
|
+
title_distances = 'Plot of Distortion from Structural Space'
|
|
400
|
+
# Print the number of clusters for the structural domain.
|
|
401
|
+
print('Structural:', number_of_clusters)
|
|
402
|
+
plt.ylabel('KMeans Distortion') # Set the y-axis label.
|
|
403
|
+
else:
|
|
404
|
+
# Define the name, directory and extension of the Distortion graph.
|
|
405
|
+
name_euc_dist_file = directory_fileoutput + \
|
|
406
|
+
'Images/Distance_Images/Cost_general.'+extension_type
|
|
407
|
+
title_distances = 'Cost Plot from General (KModes)'
|
|
408
|
+
# Print the number of clusters for the structural domain.
|
|
409
|
+
print('General:', number_of_clusters)
|
|
410
|
+
plt.ylabel('KModes Cost') # Set the y-axis label.
|
|
411
|
+
plt.title(title_distances) # Define the graph title.
|
|
412
|
+
plt.xticks(ran_curve) # Define the x-axis ticks.
|
|
413
|
+
plt.savefig(name_euc_dist_file) # Save figure.
|
|
414
|
+
plt.close() # Close plot.
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def kmeans_number_of_clusters(file, ident, directory_fileoutput, extension_type):
|
|
418
|
+
# Elbow method using the clustering cost:
|
|
419
|
+
list_clus_costs = []
|
|
420
|
+
maxcluster = 31
|
|
421
|
+
ran_curve = [x for x in range(2, maxcluster, 2)]
|
|
422
|
+
for n in range(2, maxcluster): # Calculate the cost for each n cluster.
|
|
423
|
+
km = KMeans(n_clusters=n, verbose=0,
|
|
424
|
+
init='k-means++', max_iter=300,
|
|
425
|
+
batch_size=256*int(mpi_count()),
|
|
426
|
+
compute_labels=True, random_state=2025)
|
|
427
|
+
km.fit(X=file)
|
|
428
|
+
# Costs = Clustering cost, defined as the sum distance of all points
|
|
429
|
+
# to their respective cluster centroids.
|
|
430
|
+
list_clus_costs.append(
|
|
431
|
+
np.sum(np.min(cdist(file, km.cluster_centers_, 'euclidean'), axis=1)**2) / file.shape[0])
|
|
432
|
+
# Define the x-axis coordinates of the first and last points on the line.
|
|
433
|
+
x1, y1 = 2, list_clus_costs[0]
|
|
434
|
+
# Define the y-axis coordinates of the first and last points on the line.
|
|
435
|
+
x2, y2 = 20, list_clus_costs[18]
|
|
436
|
+
|
|
437
|
+
distances = []
|
|
438
|
+
for i in range(19):
|
|
439
|
+
# Coordinate on the x-axis of the point to calculate the distance to the line.
|
|
440
|
+
x0 = i+2
|
|
441
|
+
# Coordinate on the y-axis of the point to calculate the distance to the line.
|
|
442
|
+
y0 = list_clus_costs[i]
|
|
443
|
+
numerator = abs((y2-y1)*x0 - (x2-x1)*y0 + x2*y1 - y2*x1)
|
|
444
|
+
denominator = ((y2 - y1)**2 + (x2 - x1)**2)**(1/2)
|
|
445
|
+
# Distance between a point and a line.
|
|
446
|
+
distances.append(numerator/denominator)
|
|
447
|
+
|
|
448
|
+
number_of_clusters = (distances.index(max(distances)) + 2)
|
|
449
|
+
plot_distortion(
|
|
450
|
+
list_clus_costs, directory_fileoutput,
|
|
451
|
+
ident, extension_type, number_of_clusters,
|
|
452
|
+
ran_curve)
|
|
453
|
+
return number_of_clusters
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def kmeans_clusters(file, names, ident, directory_fileoutput, extension_type):
|
|
457
|
+
# Calculate the number of clusters for the Kmodes:
|
|
458
|
+
n_clusters = kmeans_number_of_clusters(
|
|
459
|
+
file, ident, directory_fileoutput, extension_type)
|
|
460
|
+
|
|
461
|
+
# Run Kmodes with the calculated number of clusters:
|
|
462
|
+
# Parameter setting.
|
|
463
|
+
km = KMeans(n_clusters=n_clusters, verbose=0,
|
|
464
|
+
init='k-means++', max_iter=300,
|
|
465
|
+
batch_size=256*int(mpi_count()),
|
|
466
|
+
compute_labels=True, random_state=2025)
|
|
467
|
+
# Run Kmodes for the dataset and return an array with cluster labels for each molecule.
|
|
468
|
+
clusters = km.fit_predict(file)
|
|
469
|
+
clusters_list = list(clusters) # Transform np.array into a list.
|
|
470
|
+
|
|
471
|
+
# Create a dictionary with the pair "molecule name": "General cluster ID":
|
|
472
|
+
kmeans_cluster_dict = {}
|
|
473
|
+
for i, a in zip(names, clusters_list):
|
|
474
|
+
kmeans_cluster_dict[i] = int(a+1)
|
|
475
|
+
return kmeans_cluster_dict
|
|
@@ -5,11 +5,11 @@ from rdkit.Chem import AllChem
|
|
|
5
5
|
def name_extraction(file):
|
|
6
6
|
names = []
|
|
7
7
|
dict_names = {}
|
|
8
|
-
for
|
|
8
|
+
for idx, f in enumerate(file):
|
|
9
9
|
try:
|
|
10
|
-
names.append(
|
|
10
|
+
names.append(f.GetProp('_Name')) # Get name of molecules to a list
|
|
11
11
|
# Get name of molecules to a dict
|
|
12
|
-
dict_names[
|
|
12
|
+
dict_names[idx] = [f.GetProp('_Name'), f]
|
|
13
13
|
except:
|
|
14
14
|
names.append(None)
|
|
15
15
|
if None in names: # If any molecule is unnamed it returns an error.
|
|
@@ -17,8 +17,39 @@ def name_extraction(file):
|
|
|
17
17
|
print('Closing...')
|
|
18
18
|
exit()
|
|
19
19
|
else:
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
# Dealing with duplicates:
|
|
21
|
+
duplicates = {}
|
|
22
|
+
for idx, value in dict_names.items():
|
|
23
|
+
name = value[0]
|
|
24
|
+
mol = value[1]
|
|
25
|
+
if name in duplicates.keys():
|
|
26
|
+
duplicates[name].append([idx, mol])
|
|
27
|
+
else:
|
|
28
|
+
duplicates[name] = [[idx, mol]]
|
|
29
|
+
|
|
30
|
+
final_names = []
|
|
31
|
+
final_names_dict = {}
|
|
32
|
+
for k, v in duplicates.items():
|
|
33
|
+
name = k
|
|
34
|
+
replicates = v
|
|
35
|
+
if len(replicates) > 1:
|
|
36
|
+
for r in replicates:
|
|
37
|
+
index = r[0]
|
|
38
|
+
molecule = r[1]
|
|
39
|
+
final_key = f"{name}_idx-_{index}"
|
|
40
|
+
molecule.SetProp("_Name", final_key)
|
|
41
|
+
final_names_dict[final_key] = molecule
|
|
42
|
+
final_names.append(final_key)
|
|
43
|
+
else:
|
|
44
|
+
for r in replicates:
|
|
45
|
+
index = r[0]
|
|
46
|
+
molecule = r[1]
|
|
47
|
+
final_key = name
|
|
48
|
+
molecule.SetProp("_Name", final_key)
|
|
49
|
+
final_names_dict[final_key] = molecule
|
|
50
|
+
final_names.append(final_key)
|
|
51
|
+
dataframe = pd.DataFrame({'molecules': pd.Series(final_names_dict)})
|
|
52
|
+
return final_names, dataframe
|
|
22
53
|
|
|
23
54
|
|
|
24
55
|
def the_biological_handler(sdf_property_names, numberBioAct, BioActAsArgs):
|
MASSA_Algorithm/MASSAlogos.py
CHANGED
|
@@ -20,7 +20,7 @@ def initial_print():
|
|
|
20
20
|
██░░░▓▓▓▓░░░░░░░▓▓▓▓░░░░░░░▓▓▓▓░░░░█
|
|
21
21
|
██░░░░▓▓▓▓▓▓░░░░░▓▓▓▓▓▓░░░░░▓▓▓▓▓▓░░░░█ MASSA Algorithm:
|
|
22
22
|
████░░░░░▓▓▓▓░░░░░░░▓▓▓▓░░░░░░░▓▓▓▓░░░░░█████ Molecular data set sampling — Training-Test Separation
|
|
23
|
-
█▓▓▓███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███▓▓▓█ Version: v.
|
|
23
|
+
█▓▓▓███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███▓▓▓█ Version: v. 2.0.1
|
|
24
24
|
█▒▓▓▒▒▒████░░░░░░░░░░░░░░░░░░░░░░░░░░░░████▒▒▓▓▒█
|
|
25
25
|
██▓▒▒▒▓▓▓▓██████████████████████████████▓▓▓▒▒▒▒▓█ Developed by: Gabriel Corrêa Veríssimo
|
|
26
26
|
██▒▒▒▓▓▒▒▒▒▓▓▒▒▒▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▓▓▒▒▒▓▓▒▒██ LICENSE: AGPL-3.0
|
|
@@ -28,3 +28,7 @@ def initial_print():
|
|
|
28
28
|
███▓▓▓▓▒▒▒▒▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▓▓████
|
|
29
29
|
███████████████████████████████████
|
|
30
30
|
''')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def python_logo():
|
|
34
|
+
print("🍕 MASSA Algorithm v. 2.0.1: Molecular data set sampling")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from rdkit.Chem import AllChem
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def output_mols(dataframe
|
|
4
|
+
def output_mols(dataframe):
|
|
5
5
|
"""
|
|
6
6
|
For each molecule, adds the values of the calculated properties,
|
|
7
7
|
the identifications of each cluster and which set the molecule belongs to.
|
|
@@ -9,7 +9,6 @@ def output_mols(dataframe, file_output):
|
|
|
9
9
|
|
|
10
10
|
Args:
|
|
11
11
|
dataframe (pd.DataFrame): The dataframe of molecules.
|
|
12
|
-
file_output (str): The path to the output file.
|
|
13
12
|
"""
|
|
14
13
|
# Creation of dictionaries with the name of the molecule:
|
|
15
14
|
dict_molecules = dict(
|
|
@@ -41,6 +40,8 @@ def output_mols(dataframe, file_output):
|
|
|
41
40
|
|
|
42
41
|
# Add the properties to molecules from the name:molecule dictionary:
|
|
43
42
|
for i in dict_molecules.keys():
|
|
43
|
+
name = dict_molecules[i].GetProp('_Name')
|
|
44
|
+
dict_molecules[i].SetProp("_Name", name)
|
|
44
45
|
dict_molecules[i].SetProp('Set', str(dict_set[i]))
|
|
45
46
|
dict_molecules[i].SetProp('HBA count', str(dic_hba[i]))
|
|
46
47
|
dict_molecules[i].SetProp('HBD count', str(dic_hbd[i]))
|
|
@@ -62,9 +63,27 @@ def output_mols(dataframe, file_output):
|
|
|
62
63
|
|
|
63
64
|
# Create a dict_values with just the molecules
|
|
64
65
|
# (the name of the molecule is already part of molecule object):
|
|
65
|
-
|
|
66
|
+
out_mols = list(dict_molecules.values())
|
|
67
|
+
return out_mols
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def output_file_mols(dataframe, file_output):
|
|
71
|
+
"""
|
|
72
|
+
For each molecule, adds the values of the calculated properties,
|
|
73
|
+
the identifications of each cluster and which set the molecule belongs to.
|
|
74
|
+
Also, it creates the final output .sdf file.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
dataframe (pd.DataFrame): The dataframe of molecules.
|
|
78
|
+
file_output (str): The path to the output file.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# Recovers a list with just the molecules
|
|
82
|
+
# (the name of the molecule is already part of molecule object):
|
|
83
|
+
out_mols = output_mols(dataframe)
|
|
66
84
|
|
|
67
85
|
# Create output ".sdf" file:
|
|
68
86
|
with AllChem.SDWriter(file_output) as w:
|
|
69
|
-
for m in
|
|
87
|
+
for m in out_mols:
|
|
70
88
|
w.write(m)
|
|
89
|
+
return out_mols
|
MASSA_Algorithm/MASSAod.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def output_directory(directory_file_output):
|
|
4
|
+
def output_directory(directory_file_output, flag_dendrogram):
|
|
5
5
|
"""
|
|
6
6
|
Creates the output directories.
|
|
7
7
|
"""
|
|
@@ -10,13 +10,14 @@ def output_directory(directory_file_output):
|
|
|
10
10
|
except FileExistsError:
|
|
11
11
|
pass
|
|
12
12
|
try:
|
|
13
|
-
os.makedirs(directory_file_output+'Images/
|
|
14
|
-
except FileExistsError:
|
|
15
|
-
pass
|
|
16
|
-
try:
|
|
17
|
-
os.makedirs(directory_file_output+'Images/Dendrogram_HCA_Images')
|
|
13
|
+
os.makedirs(directory_file_output+'Images/Distance_Images')
|
|
18
14
|
except FileExistsError:
|
|
19
15
|
pass
|
|
16
|
+
if flag_dendrogram == True:
|
|
17
|
+
try:
|
|
18
|
+
os.makedirs(directory_file_output+'Images/Dendrogram_HCA_Images')
|
|
19
|
+
except FileExistsError:
|
|
20
|
+
pass
|
|
20
21
|
try:
|
|
21
22
|
os.makedirs(directory_file_output+'Images/Distribution')
|
|
22
23
|
except FileExistsError:
|
|
@@ -50,33 +50,33 @@ def find_name_column(columns):
|
|
|
50
50
|
Returns:
|
|
51
51
|
column_name (str): returns the name of the molecule name column.
|
|
52
52
|
"""
|
|
53
|
-
|
|
53
|
+
name_column = ""
|
|
54
54
|
for column in columns:
|
|
55
55
|
if "molecule name" == column.lower():
|
|
56
56
|
print((f"Found '{column}' column."
|
|
57
57
|
" Using it to name the molecules."))
|
|
58
58
|
name_column = column
|
|
59
|
-
|
|
59
|
+
break
|
|
60
60
|
elif "name" == column.lower():
|
|
61
61
|
print((f"Found '{column}' column."
|
|
62
62
|
" Using it to name the molecules."))
|
|
63
63
|
name_column = column
|
|
64
|
-
|
|
64
|
+
break
|
|
65
65
|
elif "molecule chembl id" == column.lower():
|
|
66
66
|
print((f"Found '{column}' column."
|
|
67
67
|
" Using it to name the molecules."))
|
|
68
68
|
name_column = column
|
|
69
|
-
|
|
69
|
+
break
|
|
70
70
|
elif "id" == column.lower():
|
|
71
71
|
print((f"Found '{column}' column."
|
|
72
72
|
" Using it to name the molecules."))
|
|
73
73
|
name_column = column
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
break
|
|
75
|
+
if name_column == "":
|
|
76
|
+
print(("Unable to find a column with molecule names."
|
|
77
|
+
" Using the index to name the molecules."))
|
|
78
|
+
name_column = "index"
|
|
79
|
+
return name_column
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
def read_df_smiles(df, write_log, drop_errors):
|
|
@@ -94,7 +94,6 @@ def read_df_smiles(df, write_log, drop_errors):
|
|
|
94
94
|
smi_name_column = find_smiles_column(columns)
|
|
95
95
|
name_name_column = find_name_column(columns)
|
|
96
96
|
|
|
97
|
-
# name_name_column = "index"
|
|
98
97
|
columns.remove(smi_name_column)
|
|
99
98
|
columns.remove(name_name_column)
|
|
100
99
|
|
|
@@ -132,13 +131,13 @@ def read_df_smiles(df, write_log, drop_errors):
|
|
|
132
131
|
return mols
|
|
133
132
|
|
|
134
133
|
|
|
135
|
-
def read_EXCEL_smiles(file, write_log):
|
|
134
|
+
def read_EXCEL_smiles(file, write_log, drop_errors):
|
|
136
135
|
df_supplier = pd.read_excel(file)
|
|
137
136
|
mols_sanitized = read_df_smiles(df_supplier, write_log, drop_errors)
|
|
138
137
|
return mols_sanitized
|
|
139
138
|
|
|
140
139
|
|
|
141
|
-
def read_CSV_smiles(file, write_log):
|
|
140
|
+
def read_CSV_smiles(file, write_log, drop_errors):
|
|
142
141
|
df_supplier = pd.read_csv(file, sep=",")
|
|
143
142
|
mols_sanitized = read_df_smiles(df_supplier, write_log, drop_errors)
|
|
144
143
|
return mols_sanitized
|
|
@@ -281,18 +280,18 @@ def read_molecules(file, write_log, drop_errors):
|
|
|
281
280
|
elif file.endswith(".sdf") or file.endswith(".mol"):
|
|
282
281
|
molecules = read_SDF(file, write_log, drop_errors)
|
|
283
282
|
elif file.endswith(".xlsx") or file.endswith(".xls"):
|
|
284
|
-
molecules = read_EXCEL_smiles(file, write_log)
|
|
283
|
+
molecules = read_EXCEL_smiles(file, write_log, drop_errors)
|
|
285
284
|
elif file.endswith(".csv"):
|
|
286
|
-
molecules = read_CSV_smiles(file, write_log)
|
|
285
|
+
molecules = read_CSV_smiles(file, write_log, drop_errors)
|
|
287
286
|
return molecules
|
|
288
287
|
|
|
289
288
|
|
|
290
289
|
def hydrogen_add(file):
|
|
291
290
|
molsH = []
|
|
292
291
|
for i in file:
|
|
292
|
+
# Add hydrogens keeping 3D coordenates.
|
|
293
293
|
i = AllChem.AddHs(i, addCoords=True)
|
|
294
294
|
molsH.append(i)
|
|
295
|
-
# Add hydrogens keeping 3D coordenates.
|
|
296
295
|
return molsH
|
|
297
296
|
|
|
298
297
|
|
|
@@ -46,16 +46,13 @@ def pca_maker(file, nPCS, svd_parameter):
|
|
|
46
46
|
def organize_df_clusterization(file, HCAdict, ident):
|
|
47
47
|
if ident == 'all':
|
|
48
48
|
file['Cluster_General'] = pd.Series(HCAdict)
|
|
49
|
-
return file
|
|
50
49
|
elif ident == 'bio':
|
|
51
50
|
file['Cluster_Biological'] = pd.Series(HCAdict)
|
|
52
|
-
return file
|
|
53
51
|
elif ident == 'PhCh':
|
|
54
52
|
file['Cluster_Physicochemical'] = pd.Series(HCAdict)
|
|
55
|
-
return file
|
|
56
53
|
else:
|
|
57
54
|
file['Cluster_Structural'] = pd.Series(HCAdict)
|
|
58
|
-
|
|
55
|
+
return file
|
|
59
56
|
|
|
60
57
|
|
|
61
58
|
# Create a matrix with cluster identifications for each of the three domains, in order to prepare for Kmodes.
|
MASSA_Algorithm/MASSAsplit.py
CHANGED
|
@@ -19,6 +19,7 @@ def split_train_test_sets(file, training_percent, splitting_strategy):
|
|
|
19
19
|
test_or_val_molecules: An identification of which molecules are on the validation set.
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
|
+
file["start_index"] = range(len(file))
|
|
22
23
|
if splitting_strategy == "tt":
|
|
23
24
|
# Split the set of molecules into training set and test set,
|
|
24
25
|
# preserving the cluster diversity of the total set according
|
|
@@ -68,6 +69,7 @@ def split_train_test_sets(file, training_percent, splitting_strategy):
|
|
|
68
69
|
dataset = pd.concat([training, test, validation])
|
|
69
70
|
# Define the output sets for the algorithm.
|
|
70
71
|
test_or_val_molecules = [test_molecules, validation_molecules]
|
|
72
|
+
dataset = dataset.sort_values(by="start_index")
|
|
71
73
|
return dataset, test_or_val_molecules
|
|
72
74
|
|
|
73
75
|
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
from MASSA_Algorithm import MASSAlogos
|
|
2
|
+
from MASSA_Algorithm import MASSAargs
|
|
3
|
+
from MASSA_Algorithm import MASSAod
|
|
4
|
+
from MASSA_Algorithm import MASSAopen_files
|
|
5
|
+
from MASSA_Algorithm import MASSAextraction
|
|
6
|
+
from MASSA_Algorithm import MASSAdescriptors
|
|
7
|
+
from MASSA_Algorithm import MASSApreparation
|
|
8
|
+
from MASSA_Algorithm import MASSAcluster
|
|
9
|
+
from MASSA_Algorithm import MASSAsplit
|
|
10
|
+
from MASSA_Algorithm import MASSAmoloutput
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def returns_zero(total, test, validation):
|
|
15
|
+
"""
|
|
16
|
+
Description:
|
|
17
|
+
It evaluates whether the distribution is inadequate. That is,
|
|
18
|
+
when the iterated cluster has a percentage greater than 0.5%
|
|
19
|
+
in the complete dataset but less than 0.5% in the test set.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
total (dict):
|
|
23
|
+
Percentage of molecules in each cluster for the whole dataset.
|
|
24
|
+
|
|
25
|
+
test (dict):
|
|
26
|
+
Percentage of molecules in each cluster for the test subset.
|
|
27
|
+
|
|
28
|
+
validation (dict):
|
|
29
|
+
Percentage of molecules in each cluster for the validation subset.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
definer (boolean): Returns True if the distribution is
|
|
33
|
+
inadequate, and False if it is adequate.
|
|
34
|
+
"""
|
|
35
|
+
definer = False
|
|
36
|
+
for i in total.keys():
|
|
37
|
+
if (total[i] > 0.5) and (test[i] <= 0.5):
|
|
38
|
+
# Definer = True (Distribution was not done properly).
|
|
39
|
+
definer = True
|
|
40
|
+
elif validation != None:
|
|
41
|
+
if (total[i] > 0.5) and (validation[i] <= 0.5):
|
|
42
|
+
definer = True
|
|
43
|
+
return definer
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def py_massa(
|
|
47
|
+
file_input=str(),
|
|
48
|
+
working_directory=str(),
|
|
49
|
+
job_name="MASSAoutput.sdf",
|
|
50
|
+
extension_type="svg",
|
|
51
|
+
dendrogram_xfont_size=5,
|
|
52
|
+
barplot_xfont_size=9,
|
|
53
|
+
training_percent=round(float(0.8), 3),
|
|
54
|
+
bioactivities_as_args=None,
|
|
55
|
+
npcs=0.85,
|
|
56
|
+
svd_parameter="full",
|
|
57
|
+
linkage_method="complete",
|
|
58
|
+
flag_dendrogram=True,
|
|
59
|
+
splitting_strategy="tt",
|
|
60
|
+
drop_errors=True,
|
|
61
|
+
large_library="auto"
|
|
62
|
+
):
|
|
63
|
+
# Initializing from Python.
|
|
64
|
+
# Print the program logo:
|
|
65
|
+
MASSAlogos.python_logo()
|
|
66
|
+
start_time = time.time()
|
|
67
|
+
|
|
68
|
+
# Defining the working_directory alias:
|
|
69
|
+
working_directory = f"{working_directory}/"
|
|
70
|
+
directoryfile_output = working_directory
|
|
71
|
+
|
|
72
|
+
# It creates the output directories:
|
|
73
|
+
MASSAod.output_directory(directoryfile_output, flag_dendrogram)
|
|
74
|
+
|
|
75
|
+
# Test nPCs:
|
|
76
|
+
if npcs > 1:
|
|
77
|
+
npcs = int(npcs)
|
|
78
|
+
elif (npcs > 0) and (npcs <= 1):
|
|
79
|
+
npcs = float(npcs)
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"ERROR: The defined number of main components is invalid. npcs = {str(npcs)}")
|
|
83
|
+
|
|
84
|
+
# Autodetect if the biological activities are set.
|
|
85
|
+
if isinstance(bioactivities_as_args, str):
|
|
86
|
+
number_bioact = 1
|
|
87
|
+
bioactivities_as_args = [bioactivities_as_args]
|
|
88
|
+
elif isinstance(bioactivities_as_args, list):
|
|
89
|
+
number_bioact = len(bioactivities_as_args)
|
|
90
|
+
elif bioactivities_as_args == None:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"The biological activities are not specified. bioactivities_as_args == None")
|
|
93
|
+
|
|
94
|
+
# Create log.txt file in directory:
|
|
95
|
+
log_path = directoryfile_output+'log.txt'
|
|
96
|
+
write_log = open(log_path, 'w')
|
|
97
|
+
|
|
98
|
+
if isinstance(file_input, str):
|
|
99
|
+
# Initial file management:
|
|
100
|
+
# Read molecules.
|
|
101
|
+
mols = MASSAopen_files.read_molecules(
|
|
102
|
+
file_input, write_log, drop_errors)
|
|
103
|
+
else:
|
|
104
|
+
mols = file_input
|
|
105
|
+
|
|
106
|
+
# Test if large_library is set:
|
|
107
|
+
if isinstance(large_library, str):
|
|
108
|
+
if large_library.lower() == 'true':
|
|
109
|
+
large_library = True
|
|
110
|
+
elif large_library.lower() == 'false':
|
|
111
|
+
large_library = False
|
|
112
|
+
elif large_library.lower() == 'auto':
|
|
113
|
+
if int(len(mols)) > 9999:
|
|
114
|
+
large_library = True
|
|
115
|
+
else:
|
|
116
|
+
large_library = False
|
|
117
|
+
|
|
118
|
+
# If auto detection is enable to select the initial cluster algorithm.
|
|
119
|
+
if large_library == 'auto':
|
|
120
|
+
if int(len(mols)) > 9999:
|
|
121
|
+
large_library = True
|
|
122
|
+
else:
|
|
123
|
+
large_library = False
|
|
124
|
+
|
|
125
|
+
if large_library == True:
|
|
126
|
+
# Deactivates the HCA dendrogram.
|
|
127
|
+
flag_dendrogram = False
|
|
128
|
+
|
|
129
|
+
# Extracting the property names from the ".sdf" input file.
|
|
130
|
+
sdf_property_names = MASSAopen_files.get_sdf_property_names(mols)
|
|
131
|
+
# Structure 3D management - It adds hydrogens keeping 3D coordenates.
|
|
132
|
+
mols_h = MASSAopen_files.hydrogen_add(mols)
|
|
133
|
+
|
|
134
|
+
# Extraction properties from ".sdf":
|
|
135
|
+
# It extracts the names of the molecules and creates a name:molecule dictionary and a dataframe.
|
|
136
|
+
names, dataframe = MASSAextraction.name_extraction(mols_h)
|
|
137
|
+
|
|
138
|
+
# It defines a list of what biological activities are being extracted.
|
|
139
|
+
biological_activity = MASSAextraction.the_biological_handler(
|
|
140
|
+
sdf_property_names, number_bioact, bioactivities_as_args)
|
|
141
|
+
# It adds the biological activities to the dataframe.
|
|
142
|
+
dataframe = MASSAextraction.list_activities(dataframe, biological_activity)
|
|
143
|
+
|
|
144
|
+
# Get fingeprint and other descriptors:
|
|
145
|
+
# Get physicochemical descriptors.
|
|
146
|
+
dataframe = MASSAdescriptors.physicochemical_descriptors(dataframe)
|
|
147
|
+
# Get AtomPairs fingerprint.
|
|
148
|
+
dataframe = MASSAdescriptors.atompairs_fingerprint(dataframe)
|
|
149
|
+
|
|
150
|
+
# Normalizes physicochemical and biological properties.
|
|
151
|
+
# Creates matrices for the three domains (struc, phys-chem, bio):
|
|
152
|
+
bio_matrix, phch_matrix, fp_matrix = MASSApreparation.normalizer_or_matrix(
|
|
153
|
+
dataframe, biological_activity)
|
|
154
|
+
|
|
155
|
+
# PCA:
|
|
156
|
+
# PCA for the biological domain.
|
|
157
|
+
bio_pca = MASSApreparation.pca_maker(bio_matrix, npcs, svd_parameter)
|
|
158
|
+
# PCA for the physicochemical domain.
|
|
159
|
+
phch_pca = MASSApreparation.pca_maker(phch_matrix, npcs, svd_parameter)
|
|
160
|
+
# PCA for the structural domain.
|
|
161
|
+
fp_pca = MASSApreparation.pca_maker(fp_matrix, npcs, svd_parameter)
|
|
162
|
+
|
|
163
|
+
if large_library == True:
|
|
164
|
+
# First clustering (MiniBatch-KMeans):
|
|
165
|
+
# It performs KMeans clustering for the biological domain.
|
|
166
|
+
bio_hca = MASSAcluster.kmeans_clusters(
|
|
167
|
+
bio_pca, names, 'bio', directoryfile_output, extension_type)
|
|
168
|
+
# It performs KMeans clustering for the physicochemical domain.
|
|
169
|
+
phch_hca = MASSAcluster.kmeans_clusters(
|
|
170
|
+
phch_pca, names, 'PhCh', directoryfile_output, extension_type)
|
|
171
|
+
# It performs KMeans clustering for the structural domain.
|
|
172
|
+
fp_hca = MASSAcluster.kmeans_clusters(
|
|
173
|
+
fp_pca, names, 'FP', directoryfile_output, extension_type)
|
|
174
|
+
else:
|
|
175
|
+
# First clustering (HCA):
|
|
176
|
+
# It performs HCA clustering without generating the dendrogram for the biological domain.
|
|
177
|
+
leaves_cluster_bio, bio_hca, linkage_bio, cutoff_bio = MASSAcluster.hca_clusters(
|
|
178
|
+
bio_pca, names, 'bio', directoryfile_output, extension_type, linkage_method)
|
|
179
|
+
# It performs HCA clustering without generating the dendrogram for the physicochemical domain.
|
|
180
|
+
leaves_cluster_phch, phch_hca, linkage_phch, cutoff_phch = MASSAcluster.hca_clusters(
|
|
181
|
+
phch_pca, names, 'PhCh', directoryfile_output, extension_type, linkage_method)
|
|
182
|
+
# It performs HCA clustering without generating the dendrogram for the structural domain.
|
|
183
|
+
leaves_cluster_fp, fp_hca, linkage_fp, cutoff_fp = MASSAcluster.hca_clusters(
|
|
184
|
+
fp_pca, names, 'FP', directoryfile_output, extension_type, linkage_method)
|
|
185
|
+
|
|
186
|
+
# It adds the biological cluster identification to the spreadsheet.
|
|
187
|
+
dataframe = MASSApreparation.organize_df_clusterization(
|
|
188
|
+
dataframe, bio_hca, 'bio')
|
|
189
|
+
# It adds the physicochemical cluster identification to the spreadsheet.
|
|
190
|
+
dataframe = MASSApreparation.organize_df_clusterization(
|
|
191
|
+
dataframe, phch_hca, 'PhCh')
|
|
192
|
+
# It adds the structural cluster identification to the spreadsheet.
|
|
193
|
+
dataframe = MASSApreparation.organize_df_clusterization(
|
|
194
|
+
dataframe, fp_hca, 'FP')
|
|
195
|
+
|
|
196
|
+
# Second clustering (Kmodes):
|
|
197
|
+
# It creates a matrix with cluster identifications for each of the three domains for Kmodes.
|
|
198
|
+
matrix_for_kmodes = MASSApreparation.organize_for_kmodes(dataframe)
|
|
199
|
+
# It performs Kmodes clustering for the general domain.
|
|
200
|
+
all_hca = MASSAcluster.kmodes_clusters(matrix_for_kmodes, names,
|
|
201
|
+
directoryfile_output, extension_type)
|
|
202
|
+
# It adds the general cluster identification to the spreadsheet.
|
|
203
|
+
dataframe = MASSApreparation.organize_df_clusterization(
|
|
204
|
+
dataframe, all_hca, 'all')
|
|
205
|
+
|
|
206
|
+
# Split into training, test:
|
|
207
|
+
dataframe, test_or_val_molecules = MASSAsplit.split_train_test_sets(
|
|
208
|
+
dataframe, training_percent, splitting_strategy)
|
|
209
|
+
|
|
210
|
+
# Bar plot of frequencies
|
|
211
|
+
# (Calculates the percentages of molecules in each cluster for each
|
|
212
|
+
# dataset and generates a bar graph for each domain):
|
|
213
|
+
|
|
214
|
+
# Biological Bar Plot
|
|
215
|
+
bio_total, bio_training, bio_test, bio_val = MASSAsplit.freq_clusters(
|
|
216
|
+
dataframe,
|
|
217
|
+
directoryfile_output,
|
|
218
|
+
extension_type,
|
|
219
|
+
'Cluster_Biological',
|
|
220
|
+
barplot_xfont_size,
|
|
221
|
+
splitting_strategy)
|
|
222
|
+
# Physicochemical Bar Plot
|
|
223
|
+
phch_total, phch_training, phch_test, phch_val = MASSAsplit.freq_clusters(
|
|
224
|
+
dataframe,
|
|
225
|
+
directoryfile_output,
|
|
226
|
+
extension_type,
|
|
227
|
+
'Cluster_Physicochemical',
|
|
228
|
+
barplot_xfont_size,
|
|
229
|
+
splitting_strategy)
|
|
230
|
+
# Structural Bar Plot
|
|
231
|
+
fp_total, fp_training, fp_test, fp_val = MASSAsplit.freq_clusters(
|
|
232
|
+
dataframe,
|
|
233
|
+
directoryfile_output,
|
|
234
|
+
extension_type,
|
|
235
|
+
'Cluster_Structural',
|
|
236
|
+
barplot_xfont_size,
|
|
237
|
+
splitting_strategy)
|
|
238
|
+
# General Bar Plot
|
|
239
|
+
all_total, all_training, all_test, all_val = MASSAsplit.freq_clusters(
|
|
240
|
+
dataframe,
|
|
241
|
+
directoryfile_output,
|
|
242
|
+
extension_type,
|
|
243
|
+
'Cluster_General',
|
|
244
|
+
barplot_xfont_size,
|
|
245
|
+
splitting_strategy)
|
|
246
|
+
|
|
247
|
+
# Verifying percentages:
|
|
248
|
+
# For biological domain:
|
|
249
|
+
bio_ok = returns_zero(bio_total, bio_test, bio_val)
|
|
250
|
+
# For physicochemical domain:
|
|
251
|
+
phch_ok = returns_zero(phch_total, phch_test, phch_val)
|
|
252
|
+
# For structural domain:
|
|
253
|
+
fp_ok = returns_zero(fp_total, fp_test, fp_val)
|
|
254
|
+
ok = [bio_ok, phch_ok, fp_ok]
|
|
255
|
+
max_iters = 0
|
|
256
|
+
|
|
257
|
+
# Redo the distribution in case of errors (up to 10 times):
|
|
258
|
+
while (True in ok) and (max_iters < 10):
|
|
259
|
+
# Split into training, test:
|
|
260
|
+
dataframe, test_or_val_molecules = MASSAsplit.split_train_test_sets(
|
|
261
|
+
dataframe, training_percent, splitting_strategy)
|
|
262
|
+
|
|
263
|
+
# Bar plot of frequencies (Calculates the percentages of molecules in each
|
|
264
|
+
# cluster for each dataset and generates a bar graph for each domain):
|
|
265
|
+
|
|
266
|
+
# Biological Bar Plot:
|
|
267
|
+
bio_total, bio_training, bio_test, bio_val = MASSAsplit.freq_clusters(
|
|
268
|
+
dataframe, directoryfile_output, extension_type,
|
|
269
|
+
'Cluster_Biological', barplot_xfont_size, splitting_strategy
|
|
270
|
+
)
|
|
271
|
+
# Physicochemical Bar Plot
|
|
272
|
+
phch_total, phch_training, phch_test, phch_val = MASSAsplit.freq_clusters(
|
|
273
|
+
dataframe, directoryfile_output, extension_type,
|
|
274
|
+
'Cluster_Physicochemical', barplot_xfont_size, splitting_strategy
|
|
275
|
+
)
|
|
276
|
+
# Structural Bar Plot
|
|
277
|
+
fp_total, fp_training, fp_test, fp_val = MASSAsplit.freq_clusters(
|
|
278
|
+
dataframe, directoryfile_output, extension_type,
|
|
279
|
+
'Cluster_Structural', barplot_xfont_size, splitting_strategy
|
|
280
|
+
)
|
|
281
|
+
# General Bar Plot
|
|
282
|
+
all_total, all_training, all_test, all_val = MASSAsplit.freq_clusters(
|
|
283
|
+
dataframe, directoryfile_output, extension_type,
|
|
284
|
+
'Cluster_General', barplot_xfont_size, splitting_strategy
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# Verifying percentages:
|
|
288
|
+
|
|
289
|
+
# For biological domain:
|
|
290
|
+
bio_ok = returns_zero(bio_total, bio_test, bio_val)
|
|
291
|
+
# For physicochemical domain:
|
|
292
|
+
phch_ok = returns_zero(phch_total, phch_test, phch_val)
|
|
293
|
+
# For structural domain:
|
|
294
|
+
fp_ok = returns_zero(fp_total, fp_test, fp_val)
|
|
295
|
+
ok = [bio_ok, phch_ok, fp_ok]
|
|
296
|
+
max_iters += 1
|
|
297
|
+
|
|
298
|
+
# Write distribution information to log file:
|
|
299
|
+
bio_distr = 'Biological Distribution'
|
|
300
|
+
MASSAsplit.log_of_distributions(
|
|
301
|
+
bio_distr, bio_total, bio_training,
|
|
302
|
+
bio_test, bio_val, splitting_strategy,
|
|
303
|
+
write_log
|
|
304
|
+
)
|
|
305
|
+
phch_distr = 'Physicochemical Distribution'
|
|
306
|
+
MASSAsplit.log_of_distributions(
|
|
307
|
+
phch_distr, phch_total, phch_training,
|
|
308
|
+
phch_test, phch_val, splitting_strategy,
|
|
309
|
+
write_log
|
|
310
|
+
)
|
|
311
|
+
fp_distr = 'Structural (FP) Distribution'
|
|
312
|
+
MASSAsplit.log_of_distributions(
|
|
313
|
+
fp_distr, fp_total, fp_training,
|
|
314
|
+
fp_test, fp_val, splitting_strategy,
|
|
315
|
+
write_log
|
|
316
|
+
)
|
|
317
|
+
all_distr = 'General Distribution'
|
|
318
|
+
MASSAsplit.log_of_distributions(
|
|
319
|
+
all_distr, all_total, all_training,
|
|
320
|
+
all_test, all_val, splitting_strategy,
|
|
321
|
+
write_log
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Output management:
|
|
325
|
+
# It adds, for each molecule, the values of the calculated properties, the identifications of each cluster and which set the molecule belongs to.
|
|
326
|
+
out_name = working_directory + job_name
|
|
327
|
+
out_mols = MASSAmoloutput.output_file_mols(dataframe, out_name)
|
|
328
|
+
|
|
329
|
+
# Plot HCAs:
|
|
330
|
+
if flag_dendrogram == True:
|
|
331
|
+
print('\nGenerating dendrogram images. Please wait...')
|
|
332
|
+
MASSAcluster.hca_plot(
|
|
333
|
+
linkage_bio, names, leaves_cluster_bio,
|
|
334
|
+
cutoff_bio, 'bio', directoryfile_output,
|
|
335
|
+
extension_type, dendrogram_xfont_size, test_or_val_molecules
|
|
336
|
+
) # Bio_Plot: Plot the HCA dendrogram
|
|
337
|
+
MASSAcluster.hca_plot(
|
|
338
|
+
linkage_phch, names, leaves_cluster_phch,
|
|
339
|
+
cutoff_phch, 'PhCh', directoryfile_output,
|
|
340
|
+
extension_type, dendrogram_xfont_size, test_or_val_molecules
|
|
341
|
+
) # phch_Plot: Plot the HCA dendrogram
|
|
342
|
+
MASSAcluster.hca_plot(
|
|
343
|
+
linkage_fp, names, leaves_cluster_fp,
|
|
344
|
+
cutoff_fp, 'FP', directoryfile_output,
|
|
345
|
+
extension_type, dendrogram_xfont_size, test_or_val_molecules
|
|
346
|
+
) # fp_Plot: Plot the HCA dendrogram
|
|
347
|
+
|
|
348
|
+
end_time = time.time()
|
|
349
|
+
elapsed_time = end_time - start_time
|
|
350
|
+
write_log.write(f'Completed! Elapsed time = {elapsed_time} s.')
|
|
351
|
+
write_log.close()
|
|
352
|
+
print(f'Completed! Elapsed time = {elapsed_time} s.')
|
|
353
|
+
return out_mols
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
|
-
Name:
|
|
3
|
-
Version:
|
|
2
|
+
Name: MASSA_Algorithm
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: MASSA Algorithm is a Python package to separate data sets of molecules into training and test sets, considering the diversity of structural, physicochemical and biological characteristics of these molecules.
|
|
5
5
|
Home-page: https://github.com/gcverissimo/MASSA_Algorithm
|
|
6
6
|
Author: Gabriel Corrêa Veríssimo
|
|
@@ -43,7 +43,16 @@ MASSA Algorithm: A tool for separating data sets of molecules into training and
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
## Version 1.0.0
|
|
46
|
-
Version 1.0.0 includes a code refactoring and adds the ability to split data into training, test, and validation subsets.
|
|
46
|
+
- Version 1.0.0 includes a code refactoring and adds the ability to split data into training, test, and validation subsets.
|
|
47
|
+
|
|
48
|
+
## Version 2.0.0
|
|
49
|
+
- Version 2.0.0 introduces algorithm changes to better handle large datasets. Specifically, for datasets with 10,000 molecules or more, Hierarchical Clustering Analysis (HCA) is automatically replaced by MiniBatch K-Means. This is the new default behavior, but it can be controlled using the flag -a / --large-datasets. Since dendrograms are generated from HCA, dendrogram plotting is disabled for large datasets. In such cases, the flag -f / --dendrogram-plot is automatically disabled, regardless of user input.
|
|
50
|
+
|
|
51
|
+
- This version introduces the ability to call the MASSA Algorithm from any Python script using MASSA_Algorithm.pyMASSA.py_massa(). However, instructions and documentation for doing so will be provided in future updates.
|
|
52
|
+
|
|
53
|
+
- Version 2.0.0 also addresses the "NaN/None values in y" error, which was caused by duplicate molecule names. In this version, molecules with identical names are detected and renamed by appending their original index as a suffix to ensure uniqueness.
|
|
54
|
+
|
|
55
|
+
- MASSA now preserves the input order of molecules in its output. However, molecules with chemical errors are skipped and excluded from the process.
|
|
47
56
|
|
|
48
57
|
## Instalation
|
|
49
58
|
MASSA Algorithm can be installed using pip:
|
|
@@ -69,6 +78,7 @@ python setup.py install
|
|
|
69
78
|
* scipy: >= 1.6;
|
|
70
79
|
* scikit-learn: > 0.24;
|
|
71
80
|
* kmodes:¹ >= 0.10.
|
|
81
|
+
|
|
72
82
|
Newer versions of the packages may also work, but they need to be tested.
|
|
73
83
|
NOTE: Also tested on: scikit-learn: 1.7.0, scipy: 1.16.1, numpy: 2.0.2, rdkit: 2025.03.5.
|
|
74
84
|
|
|
@@ -135,7 +145,11 @@ Optional arguments include:
|
|
|
135
145
|
* **Ignore Errors**: ```-e``` or ```--drop_errors```.
|
|
136
146
|
* Ignore chemistry errors, saving only molecules without any errors.
|
|
137
147
|
* Options = true (ignore molecule errors and log them in the log file), false (treat molecule errors as fatal and fail the execution).
|
|
138
|
-
* Default =
|
|
148
|
+
* Default = true.
|
|
149
|
+
* **Large Dataset**: ```-a``` or ```--large_dataset```.
|
|
150
|
+
* Switch the MASSA algorithm from HCA to MiniBatch-KMeans to handle large datasets.
|
|
151
|
+
* Options = auto (switches to KMeans if ≥ 10,000 molecules), false = use HCA (for smaller datasets), true = use KMeans (for large datasets).
|
|
152
|
+
* Default = auto.
|
|
139
153
|
|
|
140
154
|
#### Command line help
|
|
141
155
|
A full description of the arguments can also be viewed directly from the command line using the command:
|
|
@@ -164,3 +178,4 @@ Veríssimo, G. C.; Panteleão, S. Q.; Fernandes, P. O.; Gertrudes, J. C.; Kronen
|
|
|
164
178
|
|
|
165
179
|
## References
|
|
166
180
|
[1]: DE VOS, N. J. kmodes categorical clustering library. https://github.com/nicodv/kmodes. 2015-2021.
|
|
181
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
MASSA_Algorithm/MASSA.py,sha256=2CxEnm4DokqHVQqJvLFd0Z33EsOGA5lS7kERwCYAkj0,13333
|
|
2
|
+
MASSA_Algorithm/MASSAargs.py,sha256=rMlV-8ty-Cup7zufVaDY1QQv41azusgH_AwLJgxtEwc,12088
|
|
3
|
+
MASSA_Algorithm/MASSAcluster.py,sha256=aHYRHeuHxzGyiW3KIT4K3cCBddgnjKZj1U63cNCTv6U,21316
|
|
4
|
+
MASSA_Algorithm/MASSAdescriptors.py,sha256=g34_1CCJLwacj9qEfEyGe4Ci-5EKZc-DAHiFuEE8IC4,3066
|
|
5
|
+
MASSA_Algorithm/MASSAextraction.py,sha256=S5v9MqwLmVrw6Pke7d_gvaKgIvt8tT3U5w-Y_6q6lKw,7084
|
|
6
|
+
MASSA_Algorithm/MASSAlogos.py,sha256=c9EOJTAKTohUdSnYl30u2gogofUKtaTp02ZZls_5KaA,3629
|
|
7
|
+
MASSA_Algorithm/MASSAmoloutput.py,sha256=X-gMvIQ7oEkpdXdPEI5FevncDgnM02X4C-oQrOoaejI,3643
|
|
8
|
+
MASSA_Algorithm/MASSAod.py,sha256=C2pkFMo5icH9tKZxxsEuqjFMl8FyP7HlSaDqqKHYWHw,657
|
|
9
|
+
MASSA_Algorithm/MASSAopen_files.py,sha256=DDPA-zKmxxsRPkaAz8pKG_j3LK3acyXQAGdL2Mxhc5Q,10708
|
|
10
|
+
MASSA_Algorithm/MASSApreparation.py,sha256=PxKyZxvfZKt5lwrB2RtohwqWFLMzDPpscINZObYWqws,3476
|
|
11
|
+
MASSA_Algorithm/MASSAsplit.py,sha256=Gdch0jZY_bLmKWFWNeZ-b-Zmt_HkrdpN1ej2fU_u5gQ,16460
|
|
12
|
+
MASSA_Algorithm/__init__.py,sha256=yY3Tbs-t4HSl4I0Ev0RcHMnHU7SwljlWRveiRJkeCB8,10
|
|
13
|
+
MASSA_Algorithm/pyMASSA.py,sha256=Ow1ykPhFhYzttwuunbyf7Sxfayn0aUr6vwdE2hTAcNU,14154
|
|
14
|
+
massa_algorithm-2.0.1.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
15
|
+
massa_algorithm-2.0.1.dist-info/METADATA,sha256=CH99grh3pyZJUQGkqcwTTKlc7dQLH7GIMXiEKJkGMHw,10496
|
|
16
|
+
massa_algorithm-2.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
+
massa_algorithm-2.0.1.dist-info/entry_points.txt,sha256=oMtqswy07Di-w2uIYAEvenBKl0ovsr9AYm_yxn6LGtU,63
|
|
18
|
+
massa_algorithm-2.0.1.dist-info/top_level.txt,sha256=IZyttJ20RSD8w9nAcaTHWAvkvxHV5FGaVGB7mDrw9Cc,16
|
|
19
|
+
massa_algorithm-2.0.1.dist-info/RECORD,,
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
MASSA_Algorithm/MASSA.py,sha256=AKFJWuV43hrdFgNPSQjcg4J7NxEnu0Q0l67bxs_qDWo,11847
|
|
2
|
-
MASSA_Algorithm/MASSAargs.py,sha256=5aq7Ne0v1xZmH59Pb5n_YiyErIT6Y0rd7fS6MnY_2jk,11163
|
|
3
|
-
MASSA_Algorithm/MASSAcluster.py,sha256=yfZw1QE21NbDPv5pEeSzgCPMpOTPDQ2ZajvKrsBWDfA,13754
|
|
4
|
-
MASSA_Algorithm/MASSAdescriptors.py,sha256=g34_1CCJLwacj9qEfEyGe4Ci-5EKZc-DAHiFuEE8IC4,3066
|
|
5
|
-
MASSA_Algorithm/MASSAextraction.py,sha256=Xosjqwccbr1nXrgBXtRlUZrDFc05lFTtDUHWggbAC1k,5864
|
|
6
|
-
MASSA_Algorithm/MASSAlogos.py,sha256=tlM8S91KBJNqZZHjVJ2UmQFARclIw7VgZR7c-YdAYPw,3532
|
|
7
|
-
MASSA_Algorithm/MASSAmoloutput.py,sha256=41CvWOseca-LCUYn-tCITMY_RAmWd1f7z_9IX8c1jJo,3010
|
|
8
|
-
MASSA_Algorithm/MASSAod.py,sha256=By50oZurFH7HJGtyRuvAhgxKNPATI15pRLVm0MYh9ww,595
|
|
9
|
-
MASSA_Algorithm/MASSAopen_files.py,sha256=-naQ_m-p3ofZY2nZRNR2KsPxgS7vRqltma1Jp6W8raM,10729
|
|
10
|
-
MASSA_Algorithm/MASSApreparation.py,sha256=Bck0TV-l_x2POGekqeXTRkQ4GvIXZ53iResBvSjbMrY,3543
|
|
11
|
-
MASSA_Algorithm/MASSAsplit.py,sha256=_9i6g3y1NUurJQi9gKszowVeTFRYbhVntUu4EFkchsA,16363
|
|
12
|
-
MASSA_Algorithm/__init__.py,sha256=yY3Tbs-t4HSl4I0Ev0RcHMnHU7SwljlWRveiRJkeCB8,10
|
|
13
|
-
massa_algorithm-1.0.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
14
|
-
massa_algorithm-1.0.0.dist-info/METADATA,sha256=gY3NKENHoticjapmsz-Tb0vCwfWxR6nEysxAshzrGoY,9023
|
|
15
|
-
massa_algorithm-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
-
massa_algorithm-1.0.0.dist-info/entry_points.txt,sha256=oMtqswy07Di-w2uIYAEvenBKl0ovsr9AYm_yxn6LGtU,63
|
|
17
|
-
massa_algorithm-1.0.0.dist-info/top_level.txt,sha256=IZyttJ20RSD8w9nAcaTHWAvkvxHV5FGaVGB7mDrw9Cc,16
|
|
18
|
-
massa_algorithm-1.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|