robert 2.0.2__tar.gz → 2.1.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 (80) hide show
  1. {robert-2.0.2 → robert-2.1.0}/LICENSE +0 -0
  2. {robert-2.0.2 → robert-2.1.0}/PKG-INFO +54 -41
  3. {robert-2.0.2 → robert-2.1.0}/README.md +0 -1
  4. {robert-2.0.2 → robert-2.1.0}/robert/__init__.py +0 -0
  5. {robert-2.0.2 → robert-2.1.0}/robert/__main__.py +0 -0
  6. {robert-2.0.2 → robert-2.1.0}/robert/aqme.py +6 -4
  7. {robert-2.0.2 → robert-2.1.0}/robert/argument_parser.py +2 -4
  8. {robert-2.0.2 → robert-2.1.0}/robert/curate.py +66 -16
  9. robert-2.1.0/robert/evaluate.py +121 -0
  10. {robert-2.0.2 → robert-2.1.0}/robert/generate.py +34 -6
  11. {robert-2.0.2 → robert-2.1.0}/robert/generate_utils.py +24 -1
  12. {robert-2.0.2 → robert-2.1.0}/robert/icons/Robert_icon.png +0 -0
  13. {robert-2.0.2 → robert-2.1.0}/robert/icons/Robert_logo.jpg +0 -0
  14. {robert-2.0.2 → robert-2.1.0}/robert/icons/Robert_logo_transparent.png +0 -0
  15. {robert-2.0.2 → robert-2.1.0}/robert/icons/info_icon.png +0 -0
  16. {robert-2.0.2 → robert-2.1.0}/robert/icons/pdf_icon.png +0 -0
  17. {robert-2.0.2 → robert-2.1.0}/robert/icons/play_icon.png +0 -0
  18. {robert-2.0.2 → robert-2.1.0}/robert/icons/stop_icon.png +0 -0
  19. {robert-2.0.2 → robert-2.1.0}/robert/model_params/ADAB_params.yaml +0 -0
  20. {robert-2.0.2 → robert-2.1.0}/robert/model_params/GB_params.yaml +0 -0
  21. {robert-2.0.2 → robert-2.1.0}/robert/model_params/GP_params.yaml +0 -0
  22. {robert-2.0.2 → robert-2.1.0}/robert/model_params/MVL_params.yaml +0 -0
  23. {robert-2.0.2 → robert-2.1.0}/robert/model_params/NN_params.yaml +0 -0
  24. {robert-2.0.2 → robert-2.1.0}/robert/model_params/RF_params.yaml +0 -0
  25. {robert-2.0.2 → robert-2.1.0}/robert/model_params/VR_params.yaml +0 -0
  26. {robert-2.0.2 → robert-2.1.0}/robert/predict.py +0 -0
  27. {robert-2.0.2 → robert-2.1.0}/robert/predict_utils.py +48 -10
  28. {robert-2.0.2 → robert-2.1.0}/robert/report/Robert_icon.ico +0 -0
  29. {robert-2.0.2 → robert-2.1.0}/robert/report/Robert_logo.jpg +0 -0
  30. {robert-2.0.2 → robert-2.1.0}/robert/report/Robert_logo1.jpg +0 -0
  31. {robert-2.0.2 → robert-2.1.0}/robert/report/abbrev.png +0 -0
  32. {robert-2.0.2 → robert-2.1.0}/robert/report/adv_anal.png +0 -0
  33. {robert-2.0.2 → robert-2.1.0}/robert/report/features.png +0 -0
  34. {robert-2.0.2 → robert-2.1.0}/robert/report/generate.png +0 -0
  35. {robert-2.0.2 → robert-2.1.0}/robert/report/outliers.png +0 -0
  36. {robert-2.0.2 → robert-2.1.0}/robert/report/pred.png +0 -0
  37. {robert-2.0.2 → robert-2.1.0}/robert/report/repro.png +0 -0
  38. {robert-2.0.2 → robert-2.1.0}/robert/report/score.png +0 -0
  39. {robert-2.0.2 → robert-2.1.0}/robert/report/score_0.jpg +0 -0
  40. {robert-2.0.2 → robert-2.1.0}/robert/report/score_1.jpg +0 -0
  41. {robert-2.0.2 → robert-2.1.0}/robert/report/score_10.jpg +0 -0
  42. {robert-2.0.2 → robert-2.1.0}/robert/report/score_2.jpg +0 -0
  43. {robert-2.0.2 → robert-2.1.0}/robert/report/score_3.jpg +0 -0
  44. {robert-2.0.2 → robert-2.1.0}/robert/report/score_4.jpg +0 -0
  45. {robert-2.0.2 → robert-2.1.0}/robert/report/score_5.jpg +0 -0
  46. {robert-2.0.2 → robert-2.1.0}/robert/report/score_6.jpg +0 -0
  47. {robert-2.0.2 → robert-2.1.0}/robert/report/score_7.jpg +0 -0
  48. {robert-2.0.2 → robert-2.1.0}/robert/report/score_8.jpg +0 -0
  49. {robert-2.0.2 → robert-2.1.0}/robert/report/score_9.jpg +0 -0
  50. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_1_0.jpg +0 -0
  51. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_1_1.jpg +0 -0
  52. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_2_0.jpg +0 -0
  53. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_2_1.jpg +0 -0
  54. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_2_2.jpg +0 -0
  55. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_3_0.jpg +0 -0
  56. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_3_1.jpg +0 -0
  57. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_3_2.jpg +0 -0
  58. {robert-2.0.2 → robert-2.1.0}/robert/report/score_w_3_3.jpg +0 -0
  59. {robert-2.0.2 → robert-2.1.0}/robert/report/transpa.png +0 -0
  60. {robert-2.0.2 → robert-2.1.0}/robert/report/y_distrib.png +0 -0
  61. {robert-2.0.2 → robert-2.1.0}/robert/report.py +43 -26
  62. {robert-2.0.2 → robert-2.1.0}/robert/report_utils.py +4 -16
  63. {robert-2.0.2 → robert-2.1.0}/robert/robert.py +4 -21
  64. {robert-2.0.2 → robert-2.1.0}/robert/utils.py +464 -145
  65. {robert-2.0.2 → robert-2.1.0}/robert/verify.py +0 -0
  66. {robert-2.0.2 → robert-2.1.0}/robert.egg-info/PKG-INFO +54 -41
  67. {robert-2.0.2 → robert-2.1.0}/robert.egg-info/SOURCES.txt +0 -0
  68. {robert-2.0.2 → robert-2.1.0}/robert.egg-info/dependency_links.txt +0 -0
  69. robert-2.1.0/robert.egg-info/requires.txt +18 -0
  70. {robert-2.0.2 → robert-2.1.0}/robert.egg-info/top_level.txt +0 -0
  71. {robert-2.0.2 → robert-2.1.0}/setup.cfg +10 -10
  72. {robert-2.0.2 → robert-2.1.0}/setup.py +17 -17
  73. robert-2.1.0/tests/test_1curate.py +371 -0
  74. {robert-2.0.2 → robert-2.1.0}/tests/test_2generate.py +119 -76
  75. {robert-2.0.2 → robert-2.1.0}/tests/test_3verify.py +25 -18
  76. {robert-2.0.2 → robert-2.1.0}/tests/test_4predict.py +31 -17
  77. {robert-2.0.2 → robert-2.1.0}/tests/test_5aqme_n_full.py +40 -39
  78. robert-2.0.2/robert/evaluate.py +0 -181
  79. robert-2.0.2/robert.egg-info/requires.txt +0 -16
  80. robert-2.0.2/tests/test_1curate.py +0 -321
File without changes
@@ -1,41 +1,54 @@
1
- Metadata-Version: 2.1
2
- Name: robert
3
- Version: 2.0.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/2.0.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
- Classifier: Programming Language :: Python :: 3.11
20
- Classifier: Programming Language :: Python :: 3.12
21
- Requires-Python: >=3.10
22
- Description-Content-Type: text/markdown
23
- License-File: LICENSE
24
- Requires-Dist: PyYAML==6.0.2
25
- Requires-Dist: matplotlib==3.10.0
26
- Requires-Dist: pandas==2.2.3
27
- Requires-Dist: numpy==1.26.4
28
- Requires-Dist: progress==1.6
29
- Requires-Dist: seaborn==0.13.2
30
- Requires-Dist: scipy==1.15.0
31
- Requires-Dist: scikit-learn==1.6.0
32
- Requires-Dist: bayesian-optimization==3.0.0b1
33
- Requires-Dist: numba==0.60.0
34
- Requires-Dist: shap==0.46.0
35
- Requires-Dist: weasyprint==63.1
36
- Requires-Dist: PySide6
37
- Requires-Dist: PyMuPDF
38
- Requires-Dist: ansi2html
39
- Requires-Dist: rdkit==2024.3.3
40
-
41
- Documentation in Read The Docs: https://robert.readthedocs.io
1
+ Metadata-Version: 2.4
2
+ Name: robert
3
+ Version: 2.1.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/2.1.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.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: PyYAML==6.0.3
23
+ Requires-Dist: matplotlib==3.10.0
24
+ Requires-Dist: pandas==2.3.3
25
+ Requires-Dist: numpy==2.3.4
26
+ Requires-Dist: progress==1.6.1
27
+ Requires-Dist: seaborn==0.13.2
28
+ Requires-Dist: scipy==1.15.0
29
+ Requires-Dist: scikit-learn==1.7.2
30
+ Requires-Dist: bayesian-optimization==3.1.0
31
+ Requires-Dist: numba==0.62.1
32
+ Requires-Dist: shap==0.49.1
33
+ Requires-Dist: weasyprint==63.1
34
+ Requires-Dist: PySide6==6.9.2
35
+ Requires-Dist: PyMuPDF
36
+ Requires-Dist: ansi2html
37
+ Requires-Dist: rdkit==2025.9.1
38
+ Requires-Dist: psutil==7.0.0
39
+ Requires-Dist: requests==2.32.5
40
+ Dynamic: author
41
+ Dynamic: author-email
42
+ Dynamic: classifier
43
+ Dynamic: description
44
+ Dynamic: description-content-type
45
+ Dynamic: download-url
46
+ Dynamic: home-page
47
+ Dynamic: keywords
48
+ Dynamic: license
49
+ Dynamic: license-file
50
+ Dynamic: requires-dist
51
+ Dynamic: requires-python
52
+ Dynamic: summary
53
+
54
+ Documentation in Read The Docs: https://robert.readthedocs.io
@@ -20,7 +20,6 @@ Don't miss out the latest hands-on tutorials from our [YouTube channel](https://
20
20
  3. Install ROBERT using pip: `pip install robert`
21
21
  4. Install RDKit (only if you plan to use easyROB): `pip install rdkit`
22
22
  5. Install libraries for the PDF report `conda install -y -c conda-forge glib gtk3 pango mscorefonts`
23
- 6. (Only for compatible devices) Install Intelex accelerator: `pip install scikit-learn-intelex==2025.2.0`
24
23
  * Inexperienced users should visit the *Users with no Python experience* section in [Read the Docs](https://robert.readthedocs.io).
25
24
  ## Update the program
26
25
  1. Update to the latest version: `pip install robert --upgrade`
File without changes
File without changes
@@ -35,7 +35,7 @@ from robert.utils import (load_variables,
35
35
  )
36
36
 
37
37
  # list of potential arguments from CSV inputs in AQME
38
- aqme_args = ['charge','mult','complex_type','geom','constraints_atoms','constraints_dist','constraints_angle','constraints_dihedral']
38
+ aqme_args = ['charge','mult','complex_type','geom','constraints_atoms','constraints_dist','constraints_angle','constraints_dihedral','sample']
39
39
 
40
40
  class aqme:
41
41
  """
@@ -79,13 +79,15 @@ class aqme:
79
79
  # load database just to perform data checks (i.e. no need to run AQME if the specified y is not
80
80
  # in the database, since the program would crush in the subsequent CURATE job)
81
81
  job_type = 'aqme'
82
+ path_sdf = Path(f'{os.getcwd()}/CSEARCH/sdf_temp')
82
83
  if aqme_test:
83
84
  job_type = 'aqme_test'
85
+ if not path_sdf.exists():
86
+ path_sdf.mkdir(exist_ok=True, parents=True)
84
87
 
85
88
  # move the SDF files from the csv_name run (otherwise, the xTB calcs are repeated in csv_test)
86
89
  else:
87
- path_sdf = Path(f'{os.getcwd()}/CSEARCH/sdf_temp')
88
- if os.path.exists(path_sdf):
90
+ if path_sdf.exists():
89
91
  shutil.rmtree(path_sdf)
90
92
  path_sdf.mkdir(exist_ok=True, parents=True)
91
93
  for sdf_file in glob.glob(f'{os.getcwd()}/CSEARCH/*.sdf'):
@@ -120,7 +122,7 @@ class aqme:
120
122
  aqme_indv_name = 'AQME_indiv'
121
123
  else:
122
124
  smi_suffix = column.split("_")[1]
123
- csv_temp['code_name'] = csv_temp['code_name'] + '_' + smi_suffix
125
+ csv_temp['code_name'] = csv_temp['code_name'].astype(str) + '_' + smi_suffix
124
126
  csv_temp.to_csv(f'AQME_indiv_{smi_suffix}.csv', index=False)
125
127
  aqme_indv_name = f'AQME_indiv_{smi_suffix}'
126
128
 
@@ -19,8 +19,6 @@ var_dict = {
19
19
  "seed": 0,
20
20
  "destination": None,
21
21
  "csv_name" : '',
22
- "csv_train": '',
23
- "csv_valid": '',
24
22
  "csv_test": '',
25
23
  "y" : '',
26
24
  "discard" : [],
@@ -35,11 +33,12 @@ var_dict = {
35
33
  "test_set" : 0.2,
36
34
  "auto_test" : True,
37
35
  "auto_type": True,
36
+ "auto_fill": True,
38
37
  "model" : ['RF','GB','NN','MVL'],
39
38
  "eval_model" : 'MVL',
40
39
  "custom_params" : None,
41
40
  "type" : "reg",
42
- "split" : "even",
41
+ "split" : "auto",
43
42
  "nprocs": 8,
44
43
  "error_type" : "rmse",
45
44
  "pfi_epochs" : 5,
@@ -53,7 +52,6 @@ var_dict = {
53
52
  "repeat_kfolds" : 10,
54
53
  "alpha" : 0.05,
55
54
  "params_dir" : '',
56
- "csv_test" : '',
57
55
  "t_value" : 2,
58
56
  "shap_show" : 10,
59
57
  "pfi_show" : 10,
@@ -50,7 +50,8 @@ Parameters
50
50
  Number of repetitions for the k-fold cross-validation of the RFECV feature selector.
51
51
  auto_type : bool, default=True
52
52
  If there are only two y values, the program automatically changes the type of problem to classification.
53
-
53
+ auto_fill : bool, default = True
54
+ Complete missing values in columns with descriptors of "float" type using a KNN imputer
54
55
 
55
56
  """
56
57
  #####################################################.
@@ -97,12 +98,23 @@ class curate:
97
98
 
98
99
  # apply the correlation filters and returns the database without correlated descriptors
99
100
  if self.args.corr_filter_x or self.args.corr_filter_y:
100
- csv_df = correlation_filter(self,csv_df)
101
+ csv_df_result = correlation_filter(self,csv_df)
102
+ # Check if result is a tuple (model-specific CSVs) or single dataframe
103
+ if isinstance(csv_df_result, tuple):
104
+ csv_df, csv_df_per_model = csv_df_result
105
+ else:
106
+ csv_df = csv_df_result
107
+ csv_df_per_model = None
108
+ else:
109
+ csv_df_per_model = None
101
110
 
102
- # save the curated CSV
103
- _ = self.save_curate(csv_df)
111
+ # save the curated CSVs (one per model if RFECV is applied)
112
+ if csv_df_per_model is not None:
113
+ _ = self.save_curate((csv_df, csv_df_per_model))
114
+ else:
115
+ _ = self.save_curate(csv_df)
104
116
 
105
- # create Pearson heatmap
117
+ # create Pearson heatmap (use the general filtered dataframe)
106
118
  _ = pearson_map(self,csv_df,'curate')
107
119
 
108
120
  # finish the printing of the CURATE info file
@@ -140,21 +152,59 @@ class curate:
140
152
  '''
141
153
  Saves the curated database and options used in CURATE
142
154
  '''
143
-
144
- # saves curated database
145
- csv_basename = os.path.basename(f'{self.args.csv_name}').split('.')[0]
146
- csv_curate_name = f'{csv_basename}_CURATE.csv'
147
- csv_curate_name = self.args.destination.joinpath(csv_curate_name)
148
- _ = csv_df.to_csv(f'{csv_curate_name}', index = None, header=True)
149
- path_reduced = '/'.join(f'{csv_curate_name}'.replace('\\','/').split('/')[-2:])
150
- self.args.log.write(f'\no The curated database was stored in {path_reduced}.')
151
155
 
152
- # saves important options used in CURATE
156
+ csv_basename = os.path.basename(f'{self.args.csv_name}').split('.')[0]
157
+
158
+ # Check if csv_df is a tuple (csv_df_filtered, csv_df_per_model) from correlation_filter
159
+ if isinstance(csv_df, tuple):
160
+ csv_df_filtered, csv_df_per_model = csv_df
161
+
162
+ # Save model-specific curated databases (sorted for reproducibility)
163
+ for model in csv_df_per_model:
164
+ csv_curate_name = f'{csv_basename}_CURATE_{model}.csv'
165
+ csv_curate_name = self.args.destination.joinpath(csv_curate_name)
166
+ # Sort rows by y value for reproducibility
167
+ csv_df_to_save = csv_df_per_model[model].reset_index(drop=True).sort_values(by=self.args.y).reset_index(drop=True)
168
+ _ = csv_df_to_save.to_csv(f'{csv_curate_name}', index=None, header=True)
169
+
170
+ # y values to predict, considering that ROBERT will work with multiple values of y in future versions
171
+ if not isinstance(self.args.y,list):
172
+ count_y = 1
173
+ else:
174
+ count_y = len(self.args.y)
175
+
176
+ txt_csv = f'\n o Model {model}: {len(csv_df_per_model[model].columns)-len(self.args.ignore)-count_y} descriptors remaining:\n'
177
+ txt_csv += ' ' + ', '.join(f'{var}' for var in csv_df_per_model[model].columns if var not in self.args.ignore and var != self.args.y)
178
+ self.args.log.write(txt_csv)
179
+
180
+ self.args.log.write(f'\no Model-specific curated databases were stored in {self.args.destination}')
181
+
182
+ # Save general curated database (for reference/Pearson map, sorted for reproducibility)
183
+ csv_curate_name_general = f'{csv_basename}_CURATE.csv'
184
+ csv_curate_name_general = self.args.destination.joinpath(csv_curate_name_general)
185
+ csv_df_to_save_general = csv_df_filtered.reset_index(drop=True).sort_values(by=self.args.y).reset_index(drop=True)
186
+ _ = csv_df_to_save_general.to_csv(f'{csv_curate_name_general}', index=None, header=True)
187
+
188
+ else:
189
+ # Original behavior: save single curated database
190
+ csv_curate_name_general = f'{csv_basename}_CURATE.csv'
191
+ csv_curate_name_general = self.args.destination.joinpath(csv_curate_name_general)
192
+ _ = csv_df.to_csv(f'{csv_curate_name_general}', index=None, header=True)
193
+ path_reduced = '/'.join(f'{csv_curate_name_general}'.replace('\\','/').split('/')[-2:])
194
+ self.args.log.write(f'\no The curated database was stored in {path_reduced}.')
195
+
196
+ # Save important options used in CURATE
153
197
  options_name = f'CURATE_options.csv'
154
198
  options_name = self.args.destination.joinpath(options_name)
155
199
  options_df = pd.DataFrame()
156
200
  options_df['y'] = [self.args.y]
157
201
  options_df['ignore'] = [self.args.ignore]
158
202
  options_df['names'] = [self.args.names]
159
- options_df['csv_name'] = [csv_curate_name]
160
- _ = options_df.to_csv(f'{options_name}', index = None, header=True)
203
+ options_df['csv_name'] = [csv_curate_name_general]
204
+
205
+ # Save class label mapping if it exists (for classification with string labels)
206
+ if hasattr(self.args, 'class_0_label'):
207
+ options_df['class_0_label'] = [self.args.class_0_label]
208
+ options_df['class_1_label'] = [self.args.class_1_label]
209
+
210
+ _ = options_df.to_csv(f'{options_name}', index=None, header=True)
@@ -0,0 +1,121 @@
1
+ """
2
+ Parameters
3
+ ----------
4
+
5
+ GENERAL
6
+ +++++++
7
+
8
+ csv_name : str, default=''
9
+ Name of the CSV file containing all the points used in the model (combining train + valid + test).
10
+ A path can be provided (i.e. 'C:/Users/FOLDER/FILE.csv').
11
+ y : str, default=''
12
+ Name of the column containing the response variable in the input CSV file (i.e. 'solubility').
13
+ names : str, default=''
14
+ Column of the names for each datapoint. Names are used to print outliers.
15
+ eval_model : str, default='MVL'
16
+ ML models that can be evaluated (for now, only models from sklearn are accepted, more options will be added):
17
+ 1. 'MVL' (Multivariate lineal models, LinearRegression() in sklearn)
18
+ type : str, default='reg'
19
+ Type of the pedictions. Options:
20
+ 1. 'reg' (Regressor)
21
+ 2. 'clas' (Classifier)
22
+ seed : int, default=0
23
+ Random seed used in the ML predictor models and other protocols.
24
+ destination : str, default=None,
25
+ Directory to create the output file(s).
26
+
27
+ Affect VERIFY and PREDICT
28
+ +++++++++++++++++++++++++
29
+
30
+ kfold : int, default=5
31
+ Number of random data splits for the cross-validation of the models.
32
+ repeat_kfolds : int, default=10
33
+ Number of repetitions for the k-fold cross-validation of the models.
34
+
35
+ """
36
+ #####################################################.
37
+ # This file stores the EVALUATE class #
38
+ # used to evaluate existing models #
39
+ #####################################################.
40
+
41
+ import os
42
+ import shutil
43
+ import time
44
+ import pandas as pd
45
+ from pathlib import Path
46
+ from robert.utils import load_variables, finish_print, load_database, prepare_sets
47
+ from robert.generate_utils import set_sets
48
+
49
+
50
+ class evaluate:
51
+ """
52
+ Class containing all the functions from the EVALUATE module.
53
+
54
+ Parameters
55
+ ----------
56
+ kwargs : argument class
57
+ Specify any arguments from the EVALUATE module (for a complete list of variables, visit the ROBERT documentation)
58
+ """
59
+
60
+ def __init__(self, **kwargs):
61
+
62
+ start_time = time.time()
63
+
64
+ # load default and user-specified variables
65
+ self.args = load_variables(kwargs, "evaluate")
66
+
67
+ # clean folders from previous runs
68
+ _ = self.clean_eval()
69
+
70
+ # load database, discard user-defined descriptors and perform data checks
71
+ csv_df, csv_X, csv_y = load_database(self,self.args.csv_name,"generate",print_info=False)
72
+
73
+ # standardizes and separates an external test set
74
+ Xy_data = prepare_sets(self,csv_df,csv_X,csv_y,None,self.args.names,None,None,None,BO_opt=True)
75
+
76
+ # saves database and model params in the /GENERATE/Best_model/No_PFI folder
77
+ _ = self.save_generate(csv_df,Xy_data)
78
+
79
+ # finish the printing of the EVALUATE info file
80
+ _ = finish_print(self,start_time,'EVALUATE')
81
+
82
+ def clean_eval(self):
83
+ '''
84
+ Cleans folders from previous runs
85
+ '''
86
+
87
+ for folder in ['CURATE','GENERATE','VERIFY','PREDICT']:
88
+ eval_folder = f'{Path(os.getcwd()).joinpath(folder)}'
89
+ if os.path.exists(eval_folder):
90
+ shutil.rmtree(eval_folder)
91
+
92
+ def save_generate(self,csv_df,Xy_data):
93
+ '''
94
+ Saves database and model params in the /GENERATE/Best_model/No_PFI folder
95
+ '''
96
+
97
+ # copy database with Set column
98
+ generate_folder = Path('GENERATE/Best_model/No_PFI')
99
+ if os.path.exists(generate_folder):
100
+ shutil.rmtree(generate_folder)
101
+ Path(generate_folder).mkdir(exist_ok=True, parents=True)
102
+
103
+ # include the Set column to differentiate between train and test sets (and external test, if any)
104
+ csv_df = set_sets(csv_df,Xy_data)
105
+
106
+ _ = csv_df.to_csv(f'{generate_folder}/{self.args.eval_model}_db.csv', index = None, header=True)
107
+
108
+ # save all the parameters of the model
109
+ df_params = pd.DataFrame()
110
+ df_params['kfold'] = [self.args.kfold]
111
+ df_params['repeat_kfolds'] = [self.args.repeat_kfolds]
112
+ df_params['model'] = [self.args.eval_model]
113
+ df_params['type'] = [self.args.type]
114
+ df_params['seed'] = [self.args.seed]
115
+ df_params['y'] = [self.args.y]
116
+ df_params['names'] = [self.args.names]
117
+ df_params['error_type'] = [self.args.error_type]
118
+ df_params['params'] = '{}'
119
+ df_params['X_descriptors'] = [list(Xy_data['X_descriptors'])]
120
+
121
+ _ = df_params.to_csv(f'{generate_folder}/{self.args.eval_model}.csv', index = None, header=True)
@@ -72,14 +72,14 @@ Parameters
72
72
  Number of random data splits for the cross-validation of the models.
73
73
  repeat_kfolds : int, default=10
74
74
  Number of repetitions for the k-fold cross-validation of the models.
75
- split : str, default='even'
75
+ split : str, default= 'even' (regression) or 'rnd' (classification)
76
76
  Specifies how the data is split into training and test sets. Options:
77
77
  1. 'even': splits the data evenly into training and test sets.
78
78
  2. 'RND': randomly splits the data.
79
79
  3. 'stratified': splits the data while preserving the distribution of the target variable.
80
- 4. 'KN': uses a k-means approach to select representative samples.
81
- 5. 'EXTRA_Q1': selects the 10% lowest values.
82
- 6. 'EXTRA_Q5': selects the 10% highest values.
80
+ 4. 'KN': uses a k-means approach to select representative samples for training (good for intrapolation, bad for extrapolation).
81
+ 5. 'extra_q1': selects the 20% lowest values.
82
+ 6. 'extra_q5': selects the 20% highest values.
83
83
 
84
84
  """
85
85
  #####################################################.
@@ -87,6 +87,7 @@ Parameters
87
87
  # used in model generation #
88
88
  #####################################################.
89
89
 
90
+ import os
90
91
  import time
91
92
  from robert.utils import (
92
93
  load_variables,
@@ -145,8 +146,32 @@ class generate:
145
146
 
146
147
  self.args.log.write(f' - {cycle}/{len(self.args.model)} - ML model: {ML_model} ')
147
148
 
149
+ # Try to load model-specific curated CSV first, fall back to general CSV
150
+ # Get the base name from the original csv_name (remove path if any)
151
+ if 'CURATE' in str(self.args.csv_name):
152
+ # If csv_name is already a CURATE file, extract the original base name
153
+ csv_basename = os.path.basename(f'{self.args.csv_name}').replace('_CURATE.csv', '').replace('.csv', '')
154
+ else:
155
+ csv_basename = os.path.basename(f'{self.args.csv_name}').split('.')[0]
156
+
157
+ curate_folder = self.args.initial_dir.joinpath('CURATE')
158
+ csv_model_specific = curate_folder.joinpath(f'{csv_basename}_CURATE_{ML_model}.csv')
159
+
160
+ # Store the original csv_name temporarily
161
+ original_csv_name = self.args.csv_name
162
+
163
+ if os.path.exists(csv_model_specific):
164
+ csv_to_load = csv_model_specific
165
+ # Temporarily update csv_name to the model-specific CSV
166
+ self.args.csv_name = str(csv_model_specific)
167
+ self.args.log.write(f' o Using model-specific curated database: {os.path.basename(csv_model_specific)}')
168
+ else:
169
+ csv_to_load = self.args.csv_name
170
+ self.args.log.write(f' x Using general database (model-specific not found): {os.path.basename(self.args.csv_name)}')
171
+
148
172
  # load database, discard user-defined descriptors and perform data checks
149
- csv_df, csv_X, csv_y = load_database(self,self.args.csv_name,"generate",print_info=False)
173
+ csv_df, csv_X, csv_y = load_database(self,csv_to_load,"generate",print_info=False)
174
+
150
175
 
151
176
  # standardizes and separates an external test set
152
177
  Xy_data = prepare_sets(self,csv_df,csv_X,csv_y,None,self.args.names,None,None,None,BO_opt=True)
@@ -157,12 +182,15 @@ class generate:
157
182
  # apply the PFI descriptor filter if it's activated
158
183
  if self.args.pfi_filter:
159
184
  # load database, discard user-defined descriptors and perform data checks
160
- csv_df, csv_X, csv_y = load_database(self,self.args.csv_name,"generate",print_info=False)
185
+ csv_df, csv_X, csv_y = load_database(self,csv_to_load,"generate",print_info=False)
161
186
 
162
187
  # standardizes and separates an external test set
163
188
  Xy_data = prepare_sets(self,csv_df,csv_X,csv_y,None,self.args.names,None,None,None,BO_opt=True)
164
189
 
165
190
  _ = PFI_workflow(self, csv_df, ML_model, Xy_data)
191
+
192
+ # Restore the original csv_name
193
+ self.args.csv_name = original_csv_name
166
194
 
167
195
  cycle += 1
168
196
 
@@ -7,6 +7,7 @@ import shutil
7
7
  import pandas as pd
8
8
  import numpy as np
9
9
  import glob
10
+ import json
10
11
  from robert.utils import (
11
12
  load_params,
12
13
  PFI_filter,
@@ -50,7 +51,23 @@ def BO_workflow(self, Xy_data, csv_df, ML_model):
50
51
  db_name = self.args.destination.joinpath(f"Raw_data/No_PFI/{ML_model}_db")
51
52
  params_name = self.args.destination.joinpath(f"Raw_data/No_PFI/{ML_model.upper()}")
52
53
  _ = csv_df.to_csv(f'{db_name}.csv', index = None, header=True)
53
- bo_data_df = pd.DataFrame([bo_data])
54
+
55
+ # Convert params dict to string to avoid serialization issues
56
+ bo_data_to_save = bo_data.copy()
57
+ if 'params' in bo_data_to_save:
58
+ bo_data_to_save['params'] = json.dumps(bo_data_to_save['params'])
59
+ if 'X_descriptors' in bo_data_to_save:
60
+ bo_data_to_save['X_descriptors'] = json.dumps(bo_data_to_save['X_descriptors'])
61
+
62
+ # Save class label mapping if it exists (for classification with string labels)
63
+ if hasattr(self.args, 'class_0_label'):
64
+ bo_data_to_save['class_0_label'] = self.args.class_0_label
65
+ bo_data_to_save['class_1_label'] = self.args.class_1_label
66
+
67
+ # Save split type
68
+ bo_data_to_save['split'] = self.args.split
69
+
70
+ bo_data_df = pd.DataFrame([bo_data_to_save])
54
71
  _ = bo_data_df.to_csv(f'{params_name}.csv', index = None, header=True)
55
72
 
56
73
  return bo_data
@@ -133,6 +150,12 @@ def save_pfi_csv(self,csv_df,name_csv_hyperopt,PFI_dict,Xy_data_PFI,ML_model):
133
150
 
134
151
  name_csv_hyperopt_PFI = name_csv_hyperopt.replace('No_PFI','PFI')
135
152
  path_csv_PFI = self.args.destination.joinpath(f'{name_csv_hyperopt_PFI}_PFI')
153
+
154
+ # Save class label mapping if it exists (for classification with string labels)
155
+ if hasattr(self.args, 'class_0_label'):
156
+ PFI_dict['class_0_label'] = self.args.class_0_label
157
+ PFI_dict['class_1_label'] = self.args.class_1_label
158
+
136
159
  csv_PFI_df = pd.DataFrame([PFI_dict])
137
160
  _ = csv_PFI_df.to_csv(f'{path_csv_PFI}.csv', index = None, header=True)
138
161
 
File without changes
File without changes