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.
Files changed (74) hide show
  1. {robert-1.1.2 → robert-1.2.0}/LICENSE +0 -0
  2. {robert-1.1.2 → robert-1.2.0}/PKG-INFO +36 -36
  3. {robert-1.1.2 → robert-1.2.0}/README.md +0 -0
  4. {robert-1.1.2 → robert-1.2.0}/robert/__init__.py +0 -0
  5. {robert-1.1.2 → robert-1.2.0}/robert/__main__.py +0 -0
  6. {robert-1.1.2 → robert-1.2.0}/robert/aqme.py +0 -0
  7. {robert-1.1.2 → robert-1.2.0}/robert/argument_parser.py +7 -2
  8. {robert-1.1.2 → robert-1.2.0}/robert/curate.py +43 -79
  9. robert-1.2.0/robert/evaluate.py +179 -0
  10. {robert-1.1.2 → robert-1.2.0}/robert/generate.py +57 -27
  11. {robert-1.1.2 → robert-1.2.0}/robert/generate_utils.py +11 -9
  12. {robert-1.1.2 → robert-1.2.0}/robert/model_params/ADAB_params.yaml +0 -0
  13. {robert-1.1.2 → robert-1.2.0}/robert/model_params/GB_params.yaml +0 -0
  14. {robert-1.1.2 → robert-1.2.0}/robert/model_params/GP_params.yaml +0 -0
  15. {robert-1.1.2 → robert-1.2.0}/robert/model_params/MVL_params.yaml +0 -0
  16. {robert-1.1.2 → robert-1.2.0}/robert/model_params/NN_params.yaml +0 -0
  17. {robert-1.1.2 → robert-1.2.0}/robert/model_params/RF_params.yaml +0 -0
  18. {robert-1.1.2 → robert-1.2.0}/robert/model_params/VR_params.yaml +0 -0
  19. {robert-1.1.2 → robert-1.2.0}/robert/predict.py +18 -5
  20. {robert-1.1.2 → robert-1.2.0}/robert/predict_utils.py +478 -161
  21. {robert-1.1.2 → robert-1.2.0}/robert/report/GENERATE.png +0 -0
  22. {robert-1.1.2 → robert-1.2.0}/robert/report/Robert_icon.ico +0 -0
  23. {robert-1.1.2 → robert-1.2.0}/robert/report/Robert_logo.jpg +0 -0
  24. {robert-1.1.2 → robert-1.2.0}/robert/report/Robert_logo1.jpg +0 -0
  25. {robert-1.1.2 → robert-1.2.0}/robert/report/abbrev.png +0 -0
  26. robert-1.1.2/robert/report/VERIFY.png → robert-1.2.0/robert/report/adv_anal.png +0 -0
  27. robert-1.1.2/robert/report/AQME.png → robert-1.2.0/robert/report/features.png +0 -0
  28. robert-1.1.2/robert/report/CURATE.png → robert-1.2.0/robert/report/outliers.png +0 -0
  29. robert-1.1.2/robert/report/PREDICT.png → robert-1.2.0/robert/report/pred.png +0 -0
  30. {robert-1.1.2 → robert-1.2.0}/robert/report/repro.png +0 -0
  31. {robert-1.1.2 → robert-1.2.0}/robert/report/score.png +0 -0
  32. {robert-1.1.2 → robert-1.2.0}/robert/report/score_0.jpg +0 -0
  33. {robert-1.1.2 → robert-1.2.0}/robert/report/score_1.jpg +0 -0
  34. {robert-1.1.2 → robert-1.2.0}/robert/report/score_10.jpg +0 -0
  35. {robert-1.1.2 → robert-1.2.0}/robert/report/score_2.jpg +0 -0
  36. {robert-1.1.2 → robert-1.2.0}/robert/report/score_3.jpg +0 -0
  37. {robert-1.1.2 → robert-1.2.0}/robert/report/score_4.jpg +0 -0
  38. {robert-1.1.2 → robert-1.2.0}/robert/report/score_5.jpg +0 -0
  39. {robert-1.1.2 → robert-1.2.0}/robert/report/score_6.jpg +0 -0
  40. {robert-1.1.2 → robert-1.2.0}/robert/report/score_7.jpg +0 -0
  41. {robert-1.1.2 → robert-1.2.0}/robert/report/score_8.jpg +0 -0
  42. {robert-1.1.2 → robert-1.2.0}/robert/report/score_9.jpg +0 -0
  43. robert-1.2.0/robert/report/score_w_1_0.jpg +0 -0
  44. robert-1.2.0/robert/report/score_w_1_1.jpg +0 -0
  45. robert-1.2.0/robert/report/score_w_2_0.jpg +0 -0
  46. robert-1.2.0/robert/report/score_w_2_1.jpg +0 -0
  47. robert-1.2.0/robert/report/score_w_2_2.jpg +0 -0
  48. robert-1.2.0/robert/report/score_w_3_0.jpg +0 -0
  49. robert-1.2.0/robert/report/score_w_3_1.jpg +0 -0
  50. robert-1.2.0/robert/report/score_w_3_2.jpg +0 -0
  51. robert-1.2.0/robert/report/score_w_3_3.jpg +0 -0
  52. {robert-1.1.2 → robert-1.2.0}/robert/report/transpa.png +0 -0
  53. robert-1.2.0/robert/report/y_distrib.png +0 -0
  54. robert-1.2.0/robert/report.py +1171 -0
  55. robert-1.2.0/robert/report_utils.py +1163 -0
  56. {robert-1.1.2 → robert-1.2.0}/robert/robert.py +86 -42
  57. {robert-1.1.2 → robert-1.2.0}/robert/utils.py +265 -101
  58. robert-1.2.0/robert/verify.py +467 -0
  59. {robert-1.1.2 → robert-1.2.0}/robert.egg-info/PKG-INFO +36 -36
  60. {robert-1.1.2 → robert-1.2.0}/robert.egg-info/SOURCES.txt +17 -5
  61. {robert-1.1.2 → robert-1.2.0}/robert.egg-info/dependency_links.txt +0 -0
  62. {robert-1.1.2 → robert-1.2.0}/robert.egg-info/requires.txt +1 -1
  63. {robert-1.1.2 → robert-1.2.0}/robert.egg-info/top_level.txt +0 -0
  64. {robert-1.1.2 → robert-1.2.0}/setup.cfg +10 -10
  65. {robert-1.1.2 → robert-1.2.0}/setup.py +3 -3
  66. {robert-1.1.2 → robert-1.2.0}/tests/test_1curate.py +66 -9
  67. {robert-1.1.2 → robert-1.2.0}/tests/test_2generate.py +3 -5
  68. {robert-1.1.2 → robert-1.2.0}/tests/test_3verify.py +8 -16
  69. {robert-1.1.2 → robert-1.2.0}/tests/test_4predict.py +28 -12
  70. {robert-1.1.2 → robert-1.2.0}/tests/test_5aqme_n_full.py +124 -27
  71. robert-1.2.0/tests/test_6evaluate.py +276 -0
  72. robert-1.1.2/robert/report.py +0 -736
  73. robert-1.1.2/robert/report_utils.py +0 -1107
  74. 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.1.2
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.1.2.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.0
20
- Description-Content-Type: text/markdown
21
- License-File: LICENSE
22
- Requires-Dist: PyYAML==6.0.1
23
- Requires-Dist: matplotlib==3.9.1
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
- "thres_test" : 0.25,
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 matplotlib import pyplot as plt
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
- # transform categorical descriptors
81
- csv_df = self.categorical_transform(csv_df,'curate')
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
- # apply duplicate filters (i.e., duplication of datapoints or descriptors)
84
- csv_df = self.dup_filter(csv_df)
91
+ # apply duplicate filters (i.e., duplication of datapoints or descriptors)
92
+ csv_df = self.dup_filter(csv_df)
85
93
 
86
- # apply the correlation filters and returns the database without correlated descriptors
87
- if self.args.corr_filter:
88
- csv_df = self.correlation_filter(csv_df)
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
- _ = self.pearson_map(csv_df)
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
- txt_corr += f'\n\no Descriptors reduced to one third of datapoints using RFECV: {num_descriptors} descriptors remaining'
238
- # Use RFECV with RandomForestRegressor to select the most important descriptors
239
- estimator = RandomForestRegressor(random_state=0, n_estimators=30, max_depth=10, n_jobs=None)
240
- selector = RFECV(estimator, min_features_to_select=num_descriptors, cv=KFold(n_splits=5, shuffle=True, random_state=0), n_jobs=None)
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 100 datapoints.
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 be used the points as test set during ROBERT workflows. The separation
85
- of test points occurs at random before any data splits are carried out inside the GENErATE screening.
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 WARNING! The database contains {len(csv_df[self.args.y])} datapoints, the data will be split in training and validation 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".')
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 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".')
227
-
228
- if self.args.test_set > 0:
229
- n_of_points = int(len(csv_X)*(self.args.test_set))
230
-
231
- # this number must be 0 always, as it changes everything related to random seeds across the generate module
232
- random.seed(0)
233
- test_points = random.sample(range(len(csv_X)), n_of_points)
234
-
235
- # separates the test set and reset_indexes
236
- csv_df_test = csv_df.iloc[test_points].reset_index(drop=True)
237
- csv_df = csv_df.drop(test_points, axis=0).reset_index(drop=True)
238
- csv_X = csv_X.drop(test_points, axis=0).reset_index(drop=True)
239
- csv_y = csv_y.drop(test_points, axis=0).reset_index(drop=True)
240
-
241
- else:
242
- self.args.test_set = 0
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]) < 100 and self.args.split.lower() == 'rnd':
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'\nx WARNING! The database contains {len(csv_df[self.args.y])} datapoints, KN data splitting will replace the default random splitting (too few points to reach a reliable splitting). You can use random splitting with "--auto_kn False".')
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])