robert 1.1.2__tar.gz → 1.2.0__tar.gz
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.
- {robert-1.1.2 → robert-1.2.0}/LICENSE +0 -0
- {robert-1.1.2 → robert-1.2.0}/PKG-INFO +36 -36
- {robert-1.1.2 → robert-1.2.0}/README.md +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/__init__.py +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/__main__.py +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/aqme.py +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/argument_parser.py +7 -2
- {robert-1.1.2 → robert-1.2.0}/robert/curate.py +43 -79
- robert-1.2.0/robert/evaluate.py +179 -0
- {robert-1.1.2 → robert-1.2.0}/robert/generate.py +57 -27
- {robert-1.1.2 → robert-1.2.0}/robert/generate_utils.py +11 -9
- {robert-1.1.2 → robert-1.2.0}/robert/model_params/ADAB_params.yaml +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/model_params/GB_params.yaml +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/model_params/GP_params.yaml +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/model_params/MVL_params.yaml +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/model_params/NN_params.yaml +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/model_params/RF_params.yaml +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/model_params/VR_params.yaml +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/predict.py +18 -5
- {robert-1.1.2 → robert-1.2.0}/robert/predict_utils.py +478 -161
- {robert-1.1.2 → robert-1.2.0}/robert/report/GENERATE.png +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/Robert_icon.ico +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/Robert_logo.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/Robert_logo1.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/abbrev.png +0 -0
- robert-1.1.2/robert/report/VERIFY.png → robert-1.2.0/robert/report/adv_anal.png +0 -0
- robert-1.1.2/robert/report/AQME.png → robert-1.2.0/robert/report/features.png +0 -0
- robert-1.1.2/robert/report/CURATE.png → robert-1.2.0/robert/report/outliers.png +0 -0
- robert-1.1.2/robert/report/PREDICT.png → robert-1.2.0/robert/report/pred.png +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/repro.png +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score.png +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_0.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_1.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_10.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_2.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_3.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_4.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_5.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_6.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_7.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_8.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/score_9.jpg +0 -0
- robert-1.2.0/robert/report/score_w_1_0.jpg +0 -0
- robert-1.2.0/robert/report/score_w_1_1.jpg +0 -0
- robert-1.2.0/robert/report/score_w_2_0.jpg +0 -0
- robert-1.2.0/robert/report/score_w_2_1.jpg +0 -0
- robert-1.2.0/robert/report/score_w_2_2.jpg +0 -0
- robert-1.2.0/robert/report/score_w_3_0.jpg +0 -0
- robert-1.2.0/robert/report/score_w_3_1.jpg +0 -0
- robert-1.2.0/robert/report/score_w_3_2.jpg +0 -0
- robert-1.2.0/robert/report/score_w_3_3.jpg +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert/report/transpa.png +0 -0
- robert-1.2.0/robert/report/y_distrib.png +0 -0
- robert-1.2.0/robert/report.py +1171 -0
- robert-1.2.0/robert/report_utils.py +1163 -0
- {robert-1.1.2 → robert-1.2.0}/robert/robert.py +86 -42
- {robert-1.1.2 → robert-1.2.0}/robert/utils.py +265 -101
- robert-1.2.0/robert/verify.py +467 -0
- {robert-1.1.2 → robert-1.2.0}/robert.egg-info/PKG-INFO +36 -36
- {robert-1.1.2 → robert-1.2.0}/robert.egg-info/SOURCES.txt +17 -5
- {robert-1.1.2 → robert-1.2.0}/robert.egg-info/dependency_links.txt +0 -0
- {robert-1.1.2 → robert-1.2.0}/robert.egg-info/requires.txt +1 -1
- {robert-1.1.2 → robert-1.2.0}/robert.egg-info/top_level.txt +0 -0
- {robert-1.1.2 → robert-1.2.0}/setup.cfg +10 -10
- {robert-1.1.2 → robert-1.2.0}/setup.py +3 -3
- {robert-1.1.2 → robert-1.2.0}/tests/test_1curate.py +66 -9
- {robert-1.1.2 → robert-1.2.0}/tests/test_2generate.py +3 -5
- {robert-1.1.2 → robert-1.2.0}/tests/test_3verify.py +8 -16
- {robert-1.1.2 → robert-1.2.0}/tests/test_4predict.py +28 -12
- {robert-1.1.2 → robert-1.2.0}/tests/test_5aqme_n_full.py +124 -27
- robert-1.2.0/tests/test_6evaluate.py +276 -0
- robert-1.1.2/robert/report.py +0 -736
- robert-1.1.2/robert/report_utils.py +0 -1107
- robert-1.1.2/robert/verify.py +0 -365
|
File without changes
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: robert
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: Refiner and Optimizer of a Bunch of Existing Regression Tools
|
|
5
|
-
Home-page: https://github.com/jvalegre/robert
|
|
6
|
-
Download-URL: https://github.com/jvalegre/robert/archive/refs/tags/1.
|
|
7
|
-
Author: Juan V. Alegre Requena, David Dalmau
|
|
8
|
-
Author-email: jv.alegre@csic.es
|
|
9
|
-
License: MIT
|
|
10
|
-
Keywords: workflows,machine learning,cheminformatics,data curation,prediction,automated
|
|
11
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: Topic :: Software Development :: Build Tools
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Requires-Python: >=3.
|
|
20
|
-
Description-Content-Type: text/markdown
|
|
21
|
-
License-File: LICENSE
|
|
22
|
-
Requires-Dist: PyYAML==6.0.1
|
|
23
|
-
Requires-Dist: matplotlib==3.9.
|
|
24
|
-
Requires-Dist: pandas==2.2.2
|
|
25
|
-
Requires-Dist: numpy==1.26.4
|
|
26
|
-
Requires-Dist: progress==1.6
|
|
27
|
-
Requires-Dist: seaborn==0.13.2
|
|
28
|
-
Requires-Dist: scipy==1.14.0
|
|
29
|
-
Requires-Dist: scikit-learn==1.5.1
|
|
30
|
-
Requires-Dist: hyperopt==0.2.7
|
|
31
|
-
Requires-Dist: numba==0.60.0
|
|
32
|
-
Requires-Dist: shap==0.45.1
|
|
33
|
-
Requires-Dist: mapie==0.8.6
|
|
34
|
-
Requires-Dist: weasyprint==62.3
|
|
35
|
-
|
|
36
|
-
Documentation in Read The Docs: https://robert.readthedocs.io
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: robert
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Refiner and Optimizer of a Bunch of Existing Regression Tools
|
|
5
|
+
Home-page: https://github.com/jvalegre/robert
|
|
6
|
+
Download-URL: https://github.com/jvalegre/robert/archive/refs/tags/1.2.0.tar.gz
|
|
7
|
+
Author: Juan V. Alegre Requena, David Dalmau
|
|
8
|
+
Author-email: jv.alegre@csic.es
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: workflows,machine learning,cheminformatics,data curation,prediction,automated
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: PyYAML==6.0.1
|
|
23
|
+
Requires-Dist: matplotlib==3.9.2
|
|
24
|
+
Requires-Dist: pandas==2.2.2
|
|
25
|
+
Requires-Dist: numpy==1.26.4
|
|
26
|
+
Requires-Dist: progress==1.6
|
|
27
|
+
Requires-Dist: seaborn==0.13.2
|
|
28
|
+
Requires-Dist: scipy==1.14.0
|
|
29
|
+
Requires-Dist: scikit-learn==1.5.1
|
|
30
|
+
Requires-Dist: hyperopt==0.2.7
|
|
31
|
+
Requires-Dist: numba==0.60.0
|
|
32
|
+
Requires-Dist: shap==0.45.1
|
|
33
|
+
Requires-Dist: mapie==0.8.6
|
|
34
|
+
Requires-Dist: weasyprint==62.3
|
|
35
|
+
|
|
36
|
+
Documentation in Read The Docs: https://robert.readthedocs.io
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -15,10 +15,14 @@ var_dict = {
|
|
|
15
15
|
"aqme": False,
|
|
16
16
|
"report": False,
|
|
17
17
|
"cheers": False,
|
|
18
|
+
"evaluate": False,
|
|
18
19
|
"seed": [],
|
|
19
20
|
"generate_acc": 'mid',
|
|
20
21
|
"destination": None,
|
|
21
22
|
"csv_name" : '',
|
|
23
|
+
"csv_train": '',
|
|
24
|
+
"csv_valid": '',
|
|
25
|
+
"csv_test": '',
|
|
22
26
|
"y" : '',
|
|
23
27
|
"discard" : [],
|
|
24
28
|
"ignore" : [],
|
|
@@ -31,9 +35,11 @@ var_dict = {
|
|
|
31
35
|
"auto_test" : True,
|
|
32
36
|
"train" : [60,70,80,90],
|
|
33
37
|
"auto_kn" : True,
|
|
38
|
+
"auto_type": True,
|
|
34
39
|
"filter_train" : True,
|
|
35
40
|
"split" : "RND",
|
|
36
41
|
"model" : ['RF','GB','NN','MVL'],
|
|
42
|
+
"eval_model" : 'MVL',
|
|
37
43
|
"custom_params" : None,
|
|
38
44
|
"type" : "reg",
|
|
39
45
|
"epochs" : 0,
|
|
@@ -43,8 +49,7 @@ var_dict = {
|
|
|
43
49
|
"pfi_threshold" : 0.04,
|
|
44
50
|
"pfi_filter" : True,
|
|
45
51
|
"pfi_max" : 0,
|
|
46
|
-
"
|
|
47
|
-
"kfold" : 5,
|
|
52
|
+
"kfold" : 'auto',
|
|
48
53
|
"alpha" : 0.05,
|
|
49
54
|
"params_dir" : '',
|
|
50
55
|
"csv_test" : '',
|
|
@@ -13,6 +13,8 @@ Parameters
|
|
|
13
13
|
List containing the columns of the input CSV file that will be ignored during the curation process
|
|
14
14
|
(i.e. ['name','SMILES']). The descriptors will be included in the curated CSV file. The y value
|
|
15
15
|
is automatically ignored.
|
|
16
|
+
names : str, default=''
|
|
17
|
+
Column of the names for each datapoint. Names are used to print outliers.
|
|
16
18
|
destination : str, default=None,
|
|
17
19
|
Directory to create the output file(s).
|
|
18
20
|
varfile : str, default=None
|
|
@@ -36,6 +38,11 @@ Parameters
|
|
|
36
38
|
thres_y : float, default=0.001
|
|
37
39
|
Thresolhold to discard descriptors with poor correlation with the y values based on R**2 (i.e.
|
|
38
40
|
if thres_y=0.001, variables that show R**2 < 0.001 will be discarded).
|
|
41
|
+
kfold : int, default='auto'
|
|
42
|
+
Number of random data splits for the cross-validation of the RFECV feature selector. If 'auto', the program uses 5 splits
|
|
43
|
+
auto_type : bool, default=True
|
|
44
|
+
If there are only two y values, the program automatically changes the type of problem to classification.
|
|
45
|
+
|
|
39
46
|
|
|
40
47
|
"""
|
|
41
48
|
#####################################################.
|
|
@@ -46,15 +53,11 @@ Parameters
|
|
|
46
53
|
import time
|
|
47
54
|
import os
|
|
48
55
|
import pandas as pd
|
|
49
|
-
import numpy as np
|
|
50
56
|
from scipy import stats
|
|
51
|
-
from
|
|
52
|
-
import seaborn as sb
|
|
53
|
-
from robert.utils import load_variables, finish_print, load_database
|
|
54
|
-
from sklearn.feature_selection import SelectKBest, f_regression
|
|
57
|
+
from robert.utils import load_variables, finish_print, load_database, pearson_map, check_clas_problem
|
|
55
58
|
from sklearn.feature_selection import RFECV
|
|
56
59
|
from sklearn.model_selection import KFold
|
|
57
|
-
from sklearn.ensemble import RandomForestRegressor
|
|
60
|
+
from sklearn.ensemble import RandomForestRegressor,RandomForestClassifier
|
|
58
61
|
|
|
59
62
|
|
|
60
63
|
class curate:
|
|
@@ -77,18 +80,23 @@ class curate:
|
|
|
77
80
|
# load database, discard user-defined descriptors and perform data checks
|
|
78
81
|
csv_df = load_database(self,self.args.csv_name,"curate")
|
|
79
82
|
|
|
80
|
-
#
|
|
81
|
-
|
|
83
|
+
# changes type to classification if there are only two different y values
|
|
84
|
+
if self.args.type.lower() == 'reg' and self.args.auto_type:
|
|
85
|
+
self = check_clas_problem(self,csv_df)
|
|
86
|
+
|
|
87
|
+
if not self.args.evaluate:
|
|
88
|
+
# transform categorical descriptors
|
|
89
|
+
csv_df = self.categorical_transform(csv_df,'curate')
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
# apply duplicate filters (i.e., duplication of datapoints or descriptors)
|
|
92
|
+
csv_df = self.dup_filter(csv_df)
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
# apply the correlation filters and returns the database without correlated descriptors
|
|
95
|
+
if self.args.corr_filter:
|
|
96
|
+
csv_df = self.correlation_filter(csv_df)
|
|
89
97
|
|
|
90
98
|
# create Pearson heatmap
|
|
91
|
-
_ =
|
|
99
|
+
_ = pearson_map(self,csv_df,'curate')
|
|
92
100
|
|
|
93
101
|
# save the curated CSV
|
|
94
102
|
_ = self.save_curate(csv_df)
|
|
@@ -226,7 +234,7 @@ class curate:
|
|
|
226
234
|
# Check if descriptors are more than one third of datapoints
|
|
227
235
|
n_descps = len(csv_df_filtered.columns)-len(self.args.ignore)-1 # all columns - ignored - y
|
|
228
236
|
if len(csv_df[self.args.y]) > 50 and self.args.auto_test ==True:
|
|
229
|
-
datapoints= len(csv_df[self.args.y])*0.9
|
|
237
|
+
datapoints = len(csv_df[self.args.y])*0.9
|
|
230
238
|
else:
|
|
231
239
|
datapoints = len(csv_df[self.args.y])
|
|
232
240
|
if n_descps > datapoints / 3:
|
|
@@ -234,10 +242,26 @@ class curate:
|
|
|
234
242
|
# Avoid situations where the number of descriptors is equal to the number of datapoints/3
|
|
235
243
|
if len(csv_df[self.args.y]) / 3 == num_descriptors:
|
|
236
244
|
num_descriptors -= 1
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
245
|
+
# Use RFECV with a simple RandomForestRegressor to select the most important descriptors
|
|
246
|
+
if self.args.type.lower() == 'reg':
|
|
247
|
+
estimator = RandomForestRegressor(random_state=0, n_estimators=30, max_depth=10, n_jobs=None)
|
|
248
|
+
elif self.args.type.lower() == 'clas':
|
|
249
|
+
estimator = RandomForestClassifier(random_state=0, n_estimators=30, max_depth=10, n_jobs=None)
|
|
250
|
+
if self.args.kfold == 'auto':
|
|
251
|
+
# LOOCV for relatively small datasets (less than 50 datapoints)
|
|
252
|
+
if len(csv_df[self.args.y]) < 50:
|
|
253
|
+
n_splits = len(csv_df[self.args.y])
|
|
254
|
+
cv_type = 'LOOCV'
|
|
255
|
+
# k-fold CV with the same training/validation proportion used for fitting the model, using 5 splits
|
|
256
|
+
else:
|
|
257
|
+
n_splits = 5
|
|
258
|
+
cv_type = '5-fold CV'
|
|
259
|
+
else:
|
|
260
|
+
n_splits = self.args.kfold
|
|
261
|
+
cv_type = f'{n_splits}-fold CV'
|
|
262
|
+
txt_corr += f'\n\no Descriptors reduced to one third of datapoints using RFECV with {cv_type}: {num_descriptors} descriptors remaining'
|
|
263
|
+
|
|
264
|
+
selector = RFECV(estimator, min_features_to_select=num_descriptors, cv=KFold(n_splits=n_splits, shuffle=True, random_state=0), n_jobs=None)
|
|
241
265
|
X = csv_df_filtered.drop([self.args.y] + self.args.ignore, axis=1)
|
|
242
266
|
y = csv_df_filtered[self.args.y]
|
|
243
267
|
# Convert column names to strings to avoid any issues
|
|
@@ -285,63 +309,3 @@ class curate:
|
|
|
285
309
|
options_df['names'] = [self.args.names]
|
|
286
310
|
options_df['csv_name'] = [csv_curate_name]
|
|
287
311
|
_ = options_df.to_csv(f'{options_name}', index = None, header=True)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
def pearson_map(self,csv_df_pearson):
|
|
291
|
-
'''
|
|
292
|
-
Creates Pearson heatmap
|
|
293
|
-
'''
|
|
294
|
-
|
|
295
|
-
csv_df_pearson = csv_df_pearson.drop([self.args.y] + self.args.ignore, axis=1)
|
|
296
|
-
corr_matrix = csv_df_pearson.corr()
|
|
297
|
-
mask = np.zeros_like(corr_matrix, dtype=bool)
|
|
298
|
-
mask[np.triu_indices_from(mask)]= True
|
|
299
|
-
|
|
300
|
-
# these size ranges avoid matplot errors
|
|
301
|
-
if len(csv_df_pearson.columns) > 30:
|
|
302
|
-
disable_plot = True
|
|
303
|
-
else:
|
|
304
|
-
disable_plot = False
|
|
305
|
-
_, ax = plt.subplots(figsize=(7.45,6))
|
|
306
|
-
size_title = 14
|
|
307
|
-
size_font = 14-2*((len(csv_df_pearson.columns)/5))
|
|
308
|
-
|
|
309
|
-
if disable_plot:
|
|
310
|
-
self.args.log.write(f'\nx The Pearson heatmap was not generated because the number of features and the y value ({len(csv_df_pearson.columns)}) is higher than 30.')
|
|
311
|
-
else:
|
|
312
|
-
sb.set(font_scale=1.2, style='ticks')
|
|
313
|
-
|
|
314
|
-
# determines size of the letters inside the boxes (approx.)
|
|
315
|
-
annot = True
|
|
316
|
-
if len(csv_df_pearson.columns) > 30:
|
|
317
|
-
annot = False
|
|
318
|
-
|
|
319
|
-
_ = sb.heatmap(corr_matrix,
|
|
320
|
-
mask = mask,
|
|
321
|
-
square = True,
|
|
322
|
-
linewidths = .5,
|
|
323
|
-
cmap = 'coolwarm',
|
|
324
|
-
cbar = False,
|
|
325
|
-
cbar_kws = {'shrink': .4,
|
|
326
|
-
'ticks' : [-1, -.5, 0, 0.5, 1]},
|
|
327
|
-
vmin = -1,
|
|
328
|
-
vmax = 1,
|
|
329
|
-
annot = annot,
|
|
330
|
-
annot_kws = {'size': size_font})
|
|
331
|
-
|
|
332
|
-
plt.tick_params(labelsize=size_font)
|
|
333
|
-
#add the column names as labels
|
|
334
|
-
ax.set_yticklabels(corr_matrix.columns, rotation = 0)
|
|
335
|
-
ax.set_xticklabels(corr_matrix.columns)
|
|
336
|
-
|
|
337
|
-
title_fig = 'Pearson\'s r heatmap'
|
|
338
|
-
plt.title(title_fig, y=1.04, fontsize = size_title, fontweight="bold")
|
|
339
|
-
sb.set_style({'xtick.bottom': True}, {'ytick.left': True})
|
|
340
|
-
|
|
341
|
-
heatmap_name = 'Pearson_heatmap.png'
|
|
342
|
-
heatmap_path = self.args.destination.joinpath(heatmap_name)
|
|
343
|
-
plt.savefig(f'{heatmap_path}', dpi=300, bbox_inches='tight')
|
|
344
|
-
plt.clf()
|
|
345
|
-
path_reduced = '/'.join(f'{heatmap_path}'.replace('\\','/').split('/')[-2:])
|
|
346
|
-
self.args.log.write(f'\no The Pearson heatmap was stored in {path_reduced}.')
|
|
347
|
-
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parameters
|
|
3
|
+
----------
|
|
4
|
+
|
|
5
|
+
GENERAL
|
|
6
|
+
+++++++
|
|
7
|
+
|
|
8
|
+
csv_train : str, default=''
|
|
9
|
+
Name of the CSV file containing the train set. A path can be provided (i.e. 'C:/Users/FOLDER/FILE.csv').
|
|
10
|
+
csv_valid : str, default=''
|
|
11
|
+
Name of the CSV file containing the validation set. A path can be provided (i.e. 'C:/Users/FOLDER/FILE.csv').
|
|
12
|
+
csv_test : str, default=''
|
|
13
|
+
Name of the CSV file containing the test set (if any). A path can be provided (i.e. 'C:/Users/FOLDER/FILE.csv').
|
|
14
|
+
y : str, default=''
|
|
15
|
+
Name of the column containing the response variable in the input CSV file (i.e. 'solubility').
|
|
16
|
+
names : str, default=''
|
|
17
|
+
Column of the names for each datapoint. Names are used to print outliers.
|
|
18
|
+
eval_model : str, default='MVL'
|
|
19
|
+
ML models that can be evaluated (for now, only models from sklearn are accepted, more options will be added):
|
|
20
|
+
1. 'MVL' (Multivariate lineal models, LinearRegression() in sklearn)
|
|
21
|
+
type : str, default='reg'
|
|
22
|
+
Type of the pedictions. Options:
|
|
23
|
+
1. 'reg' (Regressor)
|
|
24
|
+
2. 'clas' (Classifier)
|
|
25
|
+
destination : str, default=None,
|
|
26
|
+
Directory to create the output file(s).
|
|
27
|
+
|
|
28
|
+
Affect VERIFY
|
|
29
|
+
+++++++++++++
|
|
30
|
+
|
|
31
|
+
error_type : str, default: rmse (regression), mcc (classification)
|
|
32
|
+
Target value used during the VERIFY evaluation. Options:
|
|
33
|
+
Regression:
|
|
34
|
+
1. rmse (root-mean-square error)
|
|
35
|
+
2. mae (mean absolute error)
|
|
36
|
+
3. r2 (R-squared, not recommended since R2 might be good even with high errors in small datasets)
|
|
37
|
+
Classification:
|
|
38
|
+
1. mcc (Matthew's correlation coefficient)
|
|
39
|
+
2. f1 (F1 score)
|
|
40
|
+
3. acc (accuracy, fraction of correct predictions)
|
|
41
|
+
|
|
42
|
+
Affect PREDICT
|
|
43
|
+
++++++++++++++
|
|
44
|
+
|
|
45
|
+
t_value : float, default=2
|
|
46
|
+
t-value that will be the threshold to identify outliers (check tables for t-values elsewhere).
|
|
47
|
+
The higher the t-value the more restrictive the analysis will be (i.e. there will be more
|
|
48
|
+
outliers with t-value=1 than with t-value = 4).
|
|
49
|
+
alpha : float, default=0.05
|
|
50
|
+
Significance level, or probability of making a wrong decision. This parameter is related to
|
|
51
|
+
the confidence intervals (i.e. 1-alpha is the confidence interval). By default, an alpha value
|
|
52
|
+
of 0.05 is used, which corresponds to a confidence interval of 95%.
|
|
53
|
+
shap_show : int, default=10,
|
|
54
|
+
Number of descriptors shown in the plot of the SHAP analysis.
|
|
55
|
+
pfi_show : int, default=10,
|
|
56
|
+
Number of descriptors shown in the plot of the PFI analysis.
|
|
57
|
+
|
|
58
|
+
Affect VERIFY and PREDICT
|
|
59
|
+
+++++++++++++++++++++++++
|
|
60
|
+
|
|
61
|
+
kfold : int, default='auto',
|
|
62
|
+
Number of folds for the k-fold cross-validation. If 'auto', the program does
|
|
63
|
+
a LOOCV for databases with less than 50 points, and 5-fold CV for larger databases
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
#####################################################.
|
|
67
|
+
# This file stores the EVALUATE class #
|
|
68
|
+
# used to evaluate existing models #
|
|
69
|
+
#####################################################.
|
|
70
|
+
|
|
71
|
+
import os
|
|
72
|
+
import sys
|
|
73
|
+
import shutil
|
|
74
|
+
import time
|
|
75
|
+
import pandas as pd
|
|
76
|
+
from pathlib import Path
|
|
77
|
+
from robert.utils import load_variables, finish_print, load_database
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class evaluate:
|
|
81
|
+
"""
|
|
82
|
+
Class containing all the functions from the EVALUATE module.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
kwargs : argument class
|
|
87
|
+
Specify any arguments from the EVALUATE module (for a complete list of variables, visit the ROBERT documentation)
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, **kwargs):
|
|
91
|
+
|
|
92
|
+
start_time = time.time()
|
|
93
|
+
|
|
94
|
+
# load default and user-specified variables
|
|
95
|
+
self.args = load_variables(kwargs, "evaluate")
|
|
96
|
+
|
|
97
|
+
# load databases, merge them and save CSV for ROBERT workflow
|
|
98
|
+
csv_df_train,csv_df_valid = self.load_sets()
|
|
99
|
+
|
|
100
|
+
# saves database and model params in the /GENERATE/Best_model/No_PFI folder
|
|
101
|
+
_ = self.save_generate(csv_df_train,csv_df_valid)
|
|
102
|
+
|
|
103
|
+
# finish the printing of the EVALUATE info file
|
|
104
|
+
_ = finish_print(self,start_time,'EVALUATE')
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def load_sets(self):
|
|
108
|
+
'''
|
|
109
|
+
Load databases, merge them and save CSV (tracking the test types using the Set column)
|
|
110
|
+
'''
|
|
111
|
+
|
|
112
|
+
csv_df_train = load_database(self,self.args.csv_train,"evaluate")
|
|
113
|
+
csv_df_train['Set'] = ['Training'] * len(csv_df_train[self.args.y])
|
|
114
|
+
csv_df_valid = load_database(self,self.args.csv_valid,"evaluate")
|
|
115
|
+
csv_df_valid['Set'] = ['Validation'] * len(csv_df_valid[self.args.y])
|
|
116
|
+
if self.args.csv_test != '':
|
|
117
|
+
csv_df_test = load_database(self,self.args.csv_test,"evaluate")
|
|
118
|
+
csv_df_test['Set'] = ['Test'] * len(csv_df_test[self.args.y])
|
|
119
|
+
|
|
120
|
+
self.args.ignore.append('Set')
|
|
121
|
+
|
|
122
|
+
# ensure that the datasets have the same number of columns and save combined database
|
|
123
|
+
if len(csv_df_train.columns) != len(csv_df_valid.columns):
|
|
124
|
+
self.args.log.write(f"\nx Training and validation CSV files do not have the same number of columns!")
|
|
125
|
+
self.args.log.finalize()
|
|
126
|
+
sys.exit()
|
|
127
|
+
elif self.args.csv_test != '' and len(csv_df_train.columns) != len(csv_df_test.columns):
|
|
128
|
+
self.args.log.write(f"\nx Training and test CSV files do not have the same number of columns!")
|
|
129
|
+
self.args.log.finalize()
|
|
130
|
+
sys.exit()
|
|
131
|
+
|
|
132
|
+
csv_df = pd.concat([csv_df_train,csv_df_valid], axis=0).reset_index(drop=True)
|
|
133
|
+
if self.args.csv_test != '':
|
|
134
|
+
csv_df = pd.concat([csv_df,csv_df_test], axis=0).reset_index(drop=True)
|
|
135
|
+
|
|
136
|
+
csv_basename = os.path.basename(f'{self.args.csv_train}').split('.')[0]
|
|
137
|
+
self.args.csv_name = f'{csv_basename}_EVAL_db.csv'
|
|
138
|
+
_ = csv_df.to_csv(f'{self.args.csv_name}', index = None, header=True)
|
|
139
|
+
|
|
140
|
+
return csv_df_train,csv_df_valid
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def save_generate(self,csv_df_train,csv_df_valid):
|
|
144
|
+
'''
|
|
145
|
+
Saves database and model params in the /GENERATE/Best_model/No_PFI folder
|
|
146
|
+
'''
|
|
147
|
+
|
|
148
|
+
# copy database
|
|
149
|
+
generate_folder = Path('GENERATE/Best_model/No_PFI')
|
|
150
|
+
if os.path.exists(generate_folder):
|
|
151
|
+
shutil.rmtree(generate_folder)
|
|
152
|
+
Path(generate_folder).mkdir(exist_ok=True, parents=True)
|
|
153
|
+
|
|
154
|
+
train_points = len(csv_df_train[self.args.y])
|
|
155
|
+
valid_points = len(csv_df_valid[self.args.y])
|
|
156
|
+
train_proportion = round((train_points/(train_points+valid_points))*100)
|
|
157
|
+
adapted_name = f'{self.args.eval_model}_{train_proportion}'
|
|
158
|
+
|
|
159
|
+
shutil.copy(f"{self.args.csv_name}", f"{generate_folder}/{adapted_name}_db.csv")
|
|
160
|
+
|
|
161
|
+
# save all the parameters of the model
|
|
162
|
+
df_params = pd.DataFrame()
|
|
163
|
+
train_points = len(csv_df_train[self.args.y])
|
|
164
|
+
valid_points = len(csv_df_valid[self.args.y])
|
|
165
|
+
df_params['train'] = [train_proportion]
|
|
166
|
+
df_params['split'] = ['user-defined']
|
|
167
|
+
df_params['model'] = [self.args.eval_model]
|
|
168
|
+
df_params['type'] = [self.args.type]
|
|
169
|
+
if self.args.seed == []:
|
|
170
|
+
self.args.seed = [0]
|
|
171
|
+
df_params['seed'] = [self.args.seed[0]]
|
|
172
|
+
df_params['y'] = [self.args.y]
|
|
173
|
+
df_params['names'] = [self.args.names]
|
|
174
|
+
df_params['error_type'] = [self.args.error_type]
|
|
175
|
+
# self.arg.names and "Set" are already in self.args.ignore
|
|
176
|
+
descriptors = csv_df_train.drop(self.args.ignore+[self.args.y], axis=1).columns
|
|
177
|
+
df_params['X_descriptors'] = [list(descriptors)]
|
|
178
|
+
|
|
179
|
+
_ = df_params.to_csv(f'{generate_folder}/{adapted_name}.csv', index = None, header=True)
|
|
@@ -21,7 +21,9 @@ Parameters
|
|
|
21
21
|
Proportions of the training set to use in the ML scan. The numbers are relative to the training
|
|
22
22
|
set proportion (i.e. 40 = 40% training data).
|
|
23
23
|
auto_kn : bool, default=True
|
|
24
|
-
Changes random splitting to KN splitting in databases with less than
|
|
24
|
+
Changes random splitting to KN splitting in databases with less than 250 datapoints.
|
|
25
|
+
auto_type : bool, default=True
|
|
26
|
+
If there are only two y values, the program automatically changes the type of problem to classification.
|
|
25
27
|
filter_train : bool, default=True
|
|
26
28
|
Disables the 90% training size in databases with less than 50 datapoints, and the 80% in less than 30.
|
|
27
29
|
split : str, default='RND'
|
|
@@ -81,8 +83,8 @@ Parameters
|
|
|
81
83
|
filter are used.
|
|
82
84
|
test_set : float, default=0.1
|
|
83
85
|
Amount of datapoints to separate as external test set. These points will not be used during the
|
|
84
|
-
hyperoptimization, and PREDICT will
|
|
85
|
-
|
|
86
|
+
hyperoptimization, and PREDICT will use the points as test set during ROBERT workflows. Select
|
|
87
|
+
--test_set 0 to use only training and validation.
|
|
86
88
|
auto_test : bool, default=True
|
|
87
89
|
Removes test sets in databases with less than 50 datapoints and raises % of test points to 10% if
|
|
88
90
|
test_set is lower than that.
|
|
@@ -100,6 +102,7 @@ from robert.utils import (
|
|
|
100
102
|
load_variables,
|
|
101
103
|
finish_print,
|
|
102
104
|
load_database,
|
|
105
|
+
check_clas_problem
|
|
103
106
|
)
|
|
104
107
|
from robert.generate_utils import (
|
|
105
108
|
prepare_sets,
|
|
@@ -131,6 +134,10 @@ class generate:
|
|
|
131
134
|
# load database, discard user-defined descriptors and perform data checks
|
|
132
135
|
csv_df, csv_X, csv_y = load_database(self,self.args.csv_name,"generate")
|
|
133
136
|
|
|
137
|
+
# changes type to classification if there are only two different y values
|
|
138
|
+
if self.args.type.lower() == 'reg' and self.args.auto_type:
|
|
139
|
+
self = check_clas_problem(self,csv_df)
|
|
140
|
+
|
|
134
141
|
# separates an external test set (if applicable)
|
|
135
142
|
csv_df, csv_X, csv_y, csv_df_test = self.separate_test(csv_df, csv_X, csv_y)
|
|
136
143
|
|
|
@@ -219,28 +226,49 @@ class generate:
|
|
|
219
226
|
if self.args.test_set != 0:
|
|
220
227
|
if len(csv_df[self.args.y]) < 50:
|
|
221
228
|
self.args.test_set = 0
|
|
222
|
-
self.args.log.write(f'\nx
|
|
223
|
-
self.args.test_set
|
|
229
|
+
self.args.log.write(f'\nx WARNING! The database contains {len(csv_df[self.args.y])} datapoints, the data will be split into training and validation sets with no points separated as external test set (too few points to reach a reliable splitting). You can bypass this option and include test points with "--auto_test False".')
|
|
224
230
|
elif self.args.test_set < 0.1:
|
|
225
231
|
self.args.test_set = 0.1
|
|
226
|
-
self.args.log.write(f'\nx
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
232
|
+
self.args.log.write(f'\nx WARNING! The test_set option was set to {self.args.test_set}, this value will be raised to 0.1 to include a meaningful amount of points in the test set. You can bypass this option and include less test points with "--auto_test False".')
|
|
233
|
+
|
|
234
|
+
if self.args.test_set > 0:
|
|
235
|
+
self.args.log.write(f'\no Before hyproptimization, {int(self.args.test_set*100)}% of the data was separated as test set, using an even distribution of data points across the range of y values. The remaining data points will be split into training and validation.')
|
|
236
|
+
|
|
237
|
+
n_of_points = int(len(csv_X)*(self.args.test_set))
|
|
238
|
+
|
|
239
|
+
# this number must be 0 always, as it changes everything related to random seeds across the generate module
|
|
240
|
+
random.seed(0)
|
|
241
|
+
|
|
242
|
+
# sometimes, using random selection leads to a test set with values that are not evenly
|
|
243
|
+
# distributed across the range of y values. In this part, we aim to create a test set
|
|
244
|
+
# whose values cover a significant range of the y values
|
|
245
|
+
test_points = []
|
|
246
|
+
|
|
247
|
+
# first, we divide the y values into different sections (even separations, using the number of
|
|
248
|
+
# datapoints that go into the test set)
|
|
249
|
+
sorted_csv_y = csv_y.sort_values()
|
|
250
|
+
indexes = list(sorted_csv_y.index)
|
|
251
|
+
range_section = round(len(indexes)/n_of_points) # do not use int(), the last section might get too many numbers
|
|
252
|
+
sections = {}
|
|
253
|
+
for section in range(n_of_points):
|
|
254
|
+
if section == n_of_points-1:
|
|
255
|
+
sections[section] = indexes[section*range_section:]
|
|
256
|
+
else:
|
|
257
|
+
sections[section] = indexes[section*range_section:(section+1)*range_section]
|
|
258
|
+
|
|
259
|
+
while len(test_points) < n_of_points:
|
|
260
|
+
for section in sections:
|
|
261
|
+
if len(test_points) < n_of_points:
|
|
262
|
+
new_point = random.sample(sections[section],1)[0]
|
|
263
|
+
sections[section].remove(new_point)
|
|
264
|
+
test_points.append(new_point)
|
|
265
|
+
|
|
266
|
+
# separates the test set and reset_indexes
|
|
267
|
+
csv_df_test = csv_df.iloc[test_points].reset_index(drop=True)
|
|
268
|
+
csv_df = csv_df.drop(test_points, axis=0).reset_index(drop=True)
|
|
269
|
+
csv_X = csv_X.drop(test_points, axis=0).reset_index(drop=True)
|
|
270
|
+
csv_y = csv_y.drop(test_points, axis=0).reset_index(drop=True)
|
|
271
|
+
|
|
244
272
|
return csv_df, csv_X, csv_y, csv_df_test
|
|
245
273
|
|
|
246
274
|
|
|
@@ -249,11 +277,13 @@ class generate:
|
|
|
249
277
|
Changes the split to KN when small or imbalanced databases are used
|
|
250
278
|
'''
|
|
251
279
|
|
|
252
|
-
# when using databases with a small number of points
|
|
253
|
-
if len(csv_df[self.args.y]) <
|
|
280
|
+
# when using databases with a small number of points (less than 250 datapoints)
|
|
281
|
+
if len(csv_df[self.args.y]) < 250 and self.args.split.lower() == 'rnd':
|
|
254
282
|
self.args.split = 'KN'
|
|
255
|
-
self.args.log.write(f'\
|
|
256
|
-
|
|
283
|
+
self.args.log.write(f'\no The database contains {len(csv_df[self.args.y])} datapoints, k-means data splitting will replace the default random splitting to select training and validation sets (too few points to reach a reliable splitting). You can use random splitting with "--auto_kn False".')
|
|
284
|
+
elif self.args.split.lower() == 'rnd':
|
|
285
|
+
self.args.log.write(f'\no The database contains {len(csv_df[self.args.y])} datapoints, the default random splitting will be used to select training and validation sets.')
|
|
286
|
+
|
|
257
287
|
# when using unbalanced databases (using an arbitrary imbalance ratio of 10 with the two halves of the data)
|
|
258
288
|
mid_value = max(csv_df[self.args.y])-((max(csv_df[self.args.y])-min(csv_df[self.args.y]))/2)
|
|
259
289
|
high_vals = len([i for i in csv_df[self.args.y] if i >= mid_value])
|