pico-ml 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. pico/__init__.py +3 -0
  2. pico/__main__.py +3 -0
  3. pico/cli/__init__.py +2 -0
  4. pico/cli/main.py +117 -0
  5. pico/conf/SupportedCV.py +17 -0
  6. pico/conf/SupportedModels.py +73 -0
  7. pico/conf/algo_sklearn.json +51 -0
  8. pico/conf/parameters.py +14 -0
  9. pico/domain/ClassificationDesign.py +107 -0
  10. pico/domain/Controller.py +397 -0
  11. pico/domain/DataMatrix.py +147 -0
  12. pico/domain/ExperimentDTO.py +17 -0
  13. pico/domain/MetaData.py +229 -0
  14. pico/domain/MetaboExperiment.py +696 -0
  15. pico/domain/MetaboModel.py +53 -0
  16. pico/domain/ModelFactory.py +45 -0
  17. pico/domain/Results.py +602 -0
  18. pico/domain/SplitGroup.py +202 -0
  19. pico/domain/__init__.py +9 -0
  20. pico/domain/dumps/metadata/.gitkeep +0 -0
  21. pico/domain/dumps/splits/.gitkeep +0 -0
  22. pico/service/DataFormat.py +180 -0
  23. pico/service/ExperimentDesign.py +30 -0
  24. pico/service/LoggerConfig.py +150 -0
  25. pico/service/Plots.py +472 -0
  26. pico/service/RunMLalgo.py +93 -0
  27. pico/service/SamplesPairing.py +390 -0
  28. pico/service/Utils.py +497 -0
  29. pico/service/__init__.py +7 -0
  30. pico/ui/__init__.py +1 -0
  31. pico/ui/app.py +145 -0
  32. pico/ui/assets/000_Stylesheet.css +464 -0
  33. pico/ui/assets/DecisionTree.png +0 -0
  34. pico/ui/assets/Figure_home_wider.png +0 -0
  35. pico/ui/assets/favicon.ico +0 -0
  36. pico/ui/assets/help_icon.png +0 -0
  37. pico/ui/assets/help_icon.svg +15 -0
  38. pico/ui/assets/update_figure_steps_MeDIC_4.svg +1 -0
  39. pico/ui/tabs/AggregatedResultsTab.py +394 -0
  40. pico/ui/tabs/InfoTab.py +440 -0
  41. pico/ui/tabs/InterpretTab.py +21 -0
  42. pico/ui/tabs/MLTab.py +487 -0
  43. pico/ui/tabs/MetaTab.py +23 -0
  44. pico/ui/tabs/ResultsTab.py +1062 -0
  45. pico/ui/tabs/SplitsTab.py +1227 -0
  46. pico/ui/tabs/__init__.py +6 -0
  47. pico/ui/tabs/utils.py +101 -0
  48. pico_ml-2.0.0.dist-info/METADATA +86 -0
  49. pico_ml-2.0.0.dist-info/RECORD +52 -0
  50. pico_ml-2.0.0.dist-info/WHEEL +4 -0
  51. pico_ml-2.0.0.dist-info/entry_points.txt +2 -0
  52. pico_ml-2.0.0.dist-info/licenses/LICENSE +437 -0
pico/ui/tabs/MLTab.py ADDED
@@ -0,0 +1,487 @@
1
+ import json
2
+ import re
3
+
4
+ import dash_bootstrap_components as dbc
5
+ import pandas as pd
6
+ from dash import html, State, Input, Output, dash, dcc, callback_context, ALL
7
+ from os.path import basename
8
+
9
+ from .MetaTab import MetaTab
10
+ from ...service import Utils, init_logger, log_exceptions
11
+ from ...domain import Controller
12
+
13
+
14
+ class MLTab(MetaTab):
15
+ def __init__(self, app: dash.Dash, metabo_controller: Controller):
16
+ super().__init__(app, metabo_controller)
17
+ self._logger = init_logger()
18
+
19
+ def getLayout(self) -> dbc.Tab:
20
+ __splitConfigFile = html.Div(
21
+ [
22
+ dbc.Label("Select CV search type", className="form_labels"),
23
+ dbc.RadioItems(
24
+ options=Utils.format_list_for_checklist(
25
+ self.metabo_controller.get_cv_types()
26
+ ),
27
+ value=self.metabo_controller.get_selected_cv_type(),
28
+ id="radio_cv_types",
29
+ ),
30
+ html.Br(),
31
+ html.Div(id="cv_params"),
32
+ ],
33
+ className="form_field",
34
+ )
35
+
36
+ __ThreadingConfig = html.Div(
37
+ [
38
+ dbc.Label("Multi-threading activation", className="form_labels"),
39
+ dbc.Checklist(
40
+ options=[
41
+ {"label": "Multi-threading is off", "value": 1},
42
+ ],
43
+ id="switches-input",
44
+ value=[1],
45
+ switch=True,
46
+ ),
47
+ html.Div(id="output_cv_folds_ml", style={"color": "red"}),
48
+ ],
49
+ className="form_field",
50
+ )
51
+
52
+ _definitionLearningConfig = html.Div(
53
+ className="title_and_form",
54
+ children=[
55
+ html.H4(id="Learn_conf_title", children="Define Learning configs"),
56
+ dbc.Form(
57
+ children=[
58
+ dbc.Col(
59
+ children=[
60
+ __splitConfigFile,
61
+ __ThreadingConfig,
62
+ ],
63
+ )
64
+ ]
65
+ ),
66
+ ],
67
+ )
68
+
69
+ __availableAlgorithms = html.Div(
70
+ [
71
+ dbc.Label("Available Algorithms", className="form_labels"),
72
+ dbc.Checklist(
73
+ id="in_algo_ML",
74
+ # inline=True
75
+ ),
76
+ html.Div(id="output_checklist_ml", children="", style={"color": "red"}),
77
+ ],
78
+ className="form_field",
79
+ )
80
+
81
+ __addCustomAlgorithm = html.Div(
82
+ [
83
+ dbc.Label("Add Sklearn Algorithms", className="form_labels"),
84
+ dbc.Label("from sklearn.A import B"),
85
+ dbc.Input(
86
+ id="import_new_algo",
87
+ placeholder="Complete import (A)",
88
+ className="form_input_text",
89
+ ),
90
+ dbc.Input(
91
+ id="name_new_algo",
92
+ placeholder="Enter Name (B)",
93
+ className="form_input_text",
94
+ ),
95
+ dbc.Button(
96
+ "Get attributes",
97
+ color="success",
98
+ id="get_attribute_button",
99
+ className="custom_buttons",
100
+ n_clicks=0,
101
+ ),
102
+ dbc.Button(
103
+ "Manual configuration",
104
+ color="success",
105
+ id="manual_config_button",
106
+ className="custom_buttons",
107
+ n_clicks=0,
108
+ ),
109
+ html.Div(id="output_import_algo"),
110
+ html.Div("WARNING: Incorrect configuration may lead to errors", style={"color": "orange"}),
111
+ html.Br(),
112
+ dbc.Label("Specify parameters to explore by gridsearch"),
113
+ html.Div(id="table_param"),
114
+ html.Div(id="output_error_import_algo"),
115
+ dbc.Label("Specify importance attribute"),
116
+ dbc.Select(
117
+ id="importance_attributes_dropdown_menu",
118
+ ),
119
+ dbc.Button(
120
+ "Add",
121
+ color="success",
122
+ id="add_n_refresh_sklearn_algo_button",
123
+ className="custom_buttons",
124
+ n_clicks=0,
125
+ ),
126
+ ],
127
+ className="form_field",
128
+
129
+ )
130
+
131
+ __validationButton = html.Div(
132
+ id="Learning_button_box",
133
+ className="button_box",
134
+ children=[
135
+ dcc.Loading(
136
+ id="learn_loading",
137
+ children=[
138
+ html.Div(
139
+ id="learn_loading_output",
140
+ style={"color": "green"},
141
+ )
142
+ ],
143
+ style={"width": "100%"},
144
+ type="dot",
145
+ color="#13BD00",
146
+ ),
147
+ dbc.Button(
148
+ "Learn",
149
+ color="primary",
150
+ id="start_learning_button",
151
+ className="custom_buttons",
152
+ n_clicks=0,
153
+ ),
154
+ html.Div(id="output_button_ml", children="", style={"display": "none"}),
155
+ ], )
156
+
157
+ _definitionLearningAlgorithm = html.Div(
158
+ className="title_and_form",
159
+ children=[
160
+ html.H4(id="learn_algo_title", children="Define Learning Algorithms"),
161
+ dbc.Form(
162
+ children=[
163
+ dbc.Col(
164
+ children=[
165
+ __availableAlgorithms,
166
+ # TODO: change button style
167
+ dbc.Button(
168
+ "Add custom sklearn algorithm (for experts)",
169
+ id="collapse-button",
170
+ className="mb-3",
171
+ color="outline-primary",
172
+ n_clicks=0,
173
+ ),
174
+ dbc.Collapse(
175
+ dbc.Card(dbc.CardBody(__addCustomAlgorithm)),
176
+ id="collapse",
177
+ is_open=False,
178
+ ),
179
+ __validationButton,
180
+ ],
181
+ )
182
+ ]
183
+ ),
184
+ ],
185
+ )
186
+
187
+ _learn_completed_modal = dbc.Modal(
188
+ [
189
+ dbc.ModalHeader(dbc.ModalTitle("Learning completed")),
190
+ dbc.ModalBody(id="learn_completed_body", children="The learning is completed."),
191
+ dbc.ModalFooter(
192
+ dbc.Button("Close", id="close-learn_completed_modal", className="ml-auto")
193
+ ),
194
+ ],
195
+ id="learn_completed_modal",
196
+ is_open=False,
197
+ centered=True
198
+ )
199
+
200
+ return dbc.Tab(
201
+ className="global_tab",
202
+ label="Machine Learning",
203
+ children=[
204
+ dcc.Store(id='learn-button-state', data=False),
205
+ dcc.Store(id='classification_design_filename-store', data=""),
206
+ html.Div(
207
+ className="fig_group",
208
+ children=[_definitionLearningConfig, _definitionLearningAlgorithm, _learn_completed_modal],
209
+ ),
210
+ ],
211
+ )
212
+
213
+ def _registerCallbacks(self) -> None:
214
+ @self.app.callback(
215
+ Output("in_nbr_CV_folds", "value"), [Input("custom_big_tabs", "active_tab")]
216
+ )
217
+ @log_exceptions(self._logger)
218
+ def update_nbr_CV_folds(active_tab):
219
+ if active_tab == "tab-2":
220
+ return self.metabo_controller.get_cv_folds()
221
+ return dash.no_update
222
+
223
+ @self.app.callback(
224
+ Output("switches-input", "options"), [Input("switches-input", "value")]
225
+ )
226
+ def update_switch(value):
227
+ if value == [1]:
228
+ self.metabo_controller.set_multithreading(True)
229
+ return [
230
+ {"label": "Multi-threading is on", "value": 1},
231
+ ]
232
+ elif not value:
233
+ self.metabo_controller.set_multithreading(False)
234
+ return [
235
+ {"label": "Multi-threading is off", "value": 1},
236
+ ]
237
+ return dash.no_update
238
+
239
+ @self.app.callback(
240
+ [
241
+ Output("in_algo_ML", "options"),
242
+ Output("in_algo_ML", "value"),
243
+ Output("output_checklist_ml", "children"),
244
+ Output("output_error_import_algo", "children"),
245
+ ],
246
+ [
247
+ Input("add_n_refresh_sklearn_algo_button", "n_clicks"),
248
+ Input("in_algo_ML", "value"),
249
+ Input("custom_big_tabs", "active_tab"),
250
+ ],
251
+ [
252
+ State("import_new_algo", "value"),
253
+ State("name_new_algo", "value"),
254
+ State("table_param", "children"),
255
+ State("importance_attributes_dropdown_menu", "value")
256
+ ]
257
+ )
258
+ @log_exceptions(self._logger)
259
+ def add_refresh_available_sklearn_algorithms(
260
+ n, value, active_tab, import_algo, name_algo, table_param, importance_attribute
261
+ ):
262
+ triggered_by = callback_context.triggered[0]["prop_id"].split(".")[0]
263
+ if triggered_by == "custom_big_tabs":
264
+ self._logger.info("Triggered by tab")
265
+ if active_tab == "tab-2":
266
+ self.metabo_controller.update_classification_designs_with_selected_models()
267
+
268
+ if triggered_by == "add_n_refresh_sklearn_algo_button":
269
+ grid_search_params = {}
270
+ model = Utils.get_model_from_import([import_algo], name_algo)
271
+ param_types = {param: type for param, type in Utils.get_model_parameters(model)}
272
+ error_children = []
273
+ if "{'props': {'children': 'Name', 'colSpan': 1}, 'type': 'Th', 'namespace': 'dash_html_components'}" in str(
274
+ table_param[-1]["props"]):
275
+ params_and_values = re.findall(r"{'id': '(\w+)'[\w,' :]*'value': '([\[\],. \w]+)',[\w,': ]*}",
276
+ str(table_param))
277
+ try:
278
+ grid_search_params = Utils.transform_params_to_cross_validation_dict(params_and_values,
279
+ param_types)
280
+ except ValueError as e:
281
+ for error in e.args[0]:
282
+ error_children.append(html.P(error, style={"color": "red"}))
283
+ else:
284
+ json_params_and_values = table_param[-1]["props"]["value"]
285
+ try:
286
+ params_and_values = json.loads(json_params_and_values)
287
+ except json.decoder.JSONDecodeError as e:
288
+ return dash.no_update, dash.no_update, dash.no_update, html.P("Error in parameters:" + e.msg,
289
+ style={"color": "red"})
290
+
291
+ if importance_attribute is None:
292
+ error_children.append(html.P("Please select an importance attribute", style={"color": "red"}))
293
+
294
+ if error_children:
295
+ return dash.no_update, dash.no_update, dash.no_update, error_children
296
+
297
+ self.metabo_controller.add_custom_model(
298
+ name_algo, import_algo, grid_search_params, importance_attribute
299
+ )
300
+ if triggered_by == "in_algo_ML":
301
+ self._logger.info("Triggered by dropdown")
302
+ try:
303
+ self.metabo_controller.set_selected_models(value)
304
+ except ValueError as ve:
305
+ self._logger.error(f"{ve}")
306
+ return (
307
+ Utils.format_list_for_checklist(
308
+ self.metabo_controller.get_all_algos_names()
309
+ ),
310
+ [],
311
+ str(ve),
312
+ "",
313
+ )
314
+
315
+ return (
316
+ Utils.format_list_for_checklist(
317
+ self.metabo_controller.get_all_algos_names()
318
+ ),
319
+ self.metabo_controller.get_selected_models(),
320
+ "",
321
+ "",
322
+ )
323
+
324
+ @self.app.callback(
325
+ [
326
+ Output("output_button_ml", "children"),
327
+ Output("learn_loading_output", "children"),
328
+ Output("learn-button-state", "data"),
329
+ Output('classification_design_filename-store', 'data'),
330
+ ],
331
+ [Input("start_learning_button", "n_clicks")],
332
+ )
333
+ @log_exceptions(self._logger)
334
+ def start_machine_learning(n):
335
+ if n >= 1:
336
+ self._logger.info(f"in\n{self.metabo_controller.get_selected_models()}")
337
+ self.metabo_controller.learn()
338
+
339
+ # Dump file to dump folder and to save folder (backup)
340
+ metabo_expe_filename = Utils.get_metabo_experiment_path("pico_ml")
341
+ metabo_expe_obj = self.metabo_controller.generate_save()
342
+ Utils.dump_metabo_expe(metabo_expe_obj) # Dump the classification design to the dump folder
343
+ Utils.dump_metabo_expe(metabo_expe_obj, metabo_expe_filename) # Save classification design.
344
+ del metabo_expe_obj
345
+ self._logger.info(f'The classification design file "{metabo_expe_filename}" has been saved.')
346
+ self._logger.info(f"bip bip 3 : {self.metabo_controller._metabo_experiment.classification_designs}")
347
+ return "Done!", "", True, basename(metabo_expe_filename)
348
+ else:
349
+ return dash.no_update
350
+
351
+ @self.app.callback(
352
+ Output("learn_completed_modal", "is_open"),
353
+ [Input("close-learn_completed_modal", "n_clicks"),
354
+ Input('learn-button-state', 'data')],
355
+ [State("learn_completed_modal", "is_open")]
356
+ )
357
+ def toggle_learn_completed_modal(close_clicks, learn_state, is_open):
358
+ if close_clicks or learn_state:
359
+ return not is_open
360
+ return is_open
361
+
362
+ @self.app.callback(
363
+ Output("learn_completed_body", "children"),
364
+ [Input('classification_design_filename-store', 'data')]
365
+ )
366
+ @log_exceptions(self._logger)
367
+ def update_modal_message(filename):
368
+ if filename:
369
+ return [
370
+ html.P(f'The classification design file "{filename}" has been saved.', style={'color': 'green'}),
371
+ html.P('You can go to the "Results" tabs.'),
372
+ ]
373
+ return "Error!"
374
+
375
+ @self.app.callback(
376
+ [Output("radio_cv_types", "value"),
377
+ Output("cv_params", "children")],
378
+ [Input("radio_cv_types", "value"),
379
+ Input("custom_big_tabs", "active_tab"),
380
+ Input({"type": "cv_params", "index": ALL}, "value")],
381
+ )
382
+ @log_exceptions(self._logger)
383
+ def set_cv_type(cv_value, tab, input_params):
384
+ if callback_context.triggered[0]["prop_id"] == "radio_cv_types.value":
385
+ params_form = []
386
+ self.metabo_controller.set_cv_type(cv_value)
387
+ params = self.metabo_controller.get_cv_algorithm_configuration()
388
+ if params:
389
+ for param in params:
390
+ if not param["constant"]:
391
+ name = param["name"]
392
+ value = param["value"]
393
+ type = param["type"]
394
+ if type == "int":
395
+ html_type = "number"
396
+ elif type == "float":
397
+ html_type = "number"
398
+ elif type == "bool":
399
+ html_type = "checkbox"
400
+ else:
401
+ html_type = "text"
402
+ params_form.append(
403
+ html.Tr(
404
+ [
405
+ html.Td(name),
406
+ html.Td(
407
+ dcc.Input(
408
+ id={"type": "cv_params", "index": name},
409
+ type=html_type,
410
+ value=value,
411
+ )
412
+ ),
413
+ ]
414
+ )
415
+ )
416
+ return cv_value, params_form
417
+ else:
418
+ if input_params is not None and input_params != []:
419
+ self.metabo_controller.set_cv_algorithm_configuration(input_params)
420
+
421
+ return self.metabo_controller.get_selected_cv_type(), dash.no_update
422
+
423
+ @self.app.callback(
424
+ [Output("output_import_algo", "children"),
425
+ Output("output_import_algo", "style"),
426
+ Output("table_param", "children"),
427
+ Output("importance_attributes_dropdown_menu", "options")],
428
+ [Input("get_attribute_button", "n_clicks"),
429
+ Input("manual_config_button", "n_clicks")],
430
+ [State("import_new_algo", "value"),
431
+ State("name_new_algo", "value")],
432
+ )
433
+ @log_exceptions(self._logger)
434
+ def get_attribute_algo(n_attribute, n_manual, import_new, new_algo_name):
435
+ triggered_by = callback_context.triggered[0]["prop_id"].split(".")[0]
436
+ self._logger.info(f"triggered by: [{triggered_by}]")
437
+ if n_manual > 0 or n_attribute > 0:
438
+ try:
439
+ model = Utils.get_model_from_import([import_new], new_algo_name)
440
+ except Exception as e:
441
+ self._logger.error(f"{e}")
442
+ return "Import failed: " + str(e), {"color": "red"}, "", ""
443
+ importance_attributes = [param_name for param_name, _ in
444
+ Utils.get_model_parameters_after_training(model)]
445
+ if not importance_attributes:
446
+ return "Import failed: No importance attribute found.", {"color": "red"}, "", ""
447
+ if triggered_by == "get_attribute_button":
448
+ try:
449
+ attributes = Utils.get_model_parameters(model)
450
+ attributes_table = pd.DataFrame(attributes, columns=["Name", "Type"])
451
+ attributes_table["Type"].replace(
452
+ {"str": "String", "int": "Integer", "float": "Float", "NoneType": "Unspecified"},
453
+ inplace=True)
454
+ inputs = []
455
+ for attribute, _ in attributes:
456
+ inputs.append(dbc.Input(id=attribute, type="text", placeholder="Value"))
457
+ attributes_table["Value"] = inputs
458
+
459
+ default_text = [
460
+ html.Br(),
461
+ html.P("You can set the grid search parameters as followed:"),
462
+ html.P(
463
+ "Values: 'val1A, val1B, val1C'"
464
+ ),
465
+ dbc.Table.from_dataframe(attributes_table)
466
+ ]
467
+
468
+ return f"{model.__name__} found", {
469
+ "color": "green"}, default_text, Utils.format_list_for_checklist(importance_attributes)
470
+ except Exception as e:
471
+ self._logger.info(f"{e}")
472
+ return "Import failed: " + str(e), {"color": "red"}, "", ""
473
+ elif triggered_by == "manual_config_button":
474
+ return "", None, [html.P("The following configuration must be in JSON format",
475
+ style={"color": "orange"}), html.Br(),
476
+ dcc.Textarea()], Utils.format_list_for_checklist(importance_attributes)
477
+ return dash.no_update, dash.no_update, dash.no_update, dash.no_update
478
+
479
+ @self.app.callback(
480
+ Output("collapse", "is_open"),
481
+ [Input("collapse-button", "n_clicks")],
482
+ [State("collapse", "is_open")],
483
+ )
484
+ def toggle_collapse(n, is_open):
485
+ if n:
486
+ return not is_open
487
+ return is_open
@@ -0,0 +1,23 @@
1
+ from abc import abstractmethod
2
+ import dash_bootstrap_components as dbc
3
+
4
+ from dash import Dash
5
+
6
+ from ...domain import Controller
7
+ from ...service import init_logger
8
+
9
+
10
+ class MetaTab:
11
+ def __init__(self, app: Dash, metabo_controller: Controller):
12
+ self._logger = init_logger()
13
+ self.metabo_controller = metabo_controller
14
+ self.app = app
15
+ self._registerCallbacks()
16
+
17
+ @abstractmethod
18
+ def getLayout(self) -> dbc.Tab:
19
+ pass
20
+
21
+ @abstractmethod
22
+ def _registerCallbacks(self) -> None:
23
+ pass