workbench 0.8.162__py3-none-any.whl → 0.8.220__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.

Potentially problematic release.


This version of workbench might be problematic. Click here for more details.

Files changed (147) hide show
  1. workbench/algorithms/dataframe/__init__.py +1 -2
  2. workbench/algorithms/dataframe/compound_dataset_overlap.py +321 -0
  3. workbench/algorithms/dataframe/feature_space_proximity.py +168 -75
  4. workbench/algorithms/dataframe/fingerprint_proximity.py +422 -86
  5. workbench/algorithms/dataframe/projection_2d.py +44 -21
  6. workbench/algorithms/dataframe/proximity.py +259 -305
  7. workbench/algorithms/graph/light/proximity_graph.py +14 -12
  8. workbench/algorithms/models/cleanlab_model.py +382 -0
  9. workbench/algorithms/models/noise_model.py +388 -0
  10. workbench/algorithms/sql/outliers.py +3 -3
  11. workbench/api/__init__.py +5 -1
  12. workbench/api/compound.py +1 -1
  13. workbench/api/df_store.py +17 -108
  14. workbench/api/endpoint.py +18 -5
  15. workbench/api/feature_set.py +121 -15
  16. workbench/api/meta.py +5 -2
  17. workbench/api/meta_model.py +289 -0
  18. workbench/api/model.py +55 -21
  19. workbench/api/monitor.py +1 -16
  20. workbench/api/parameter_store.py +3 -52
  21. workbench/cached/cached_model.py +4 -4
  22. workbench/core/artifacts/__init__.py +11 -2
  23. workbench/core/artifacts/artifact.py +16 -8
  24. workbench/core/artifacts/data_capture_core.py +355 -0
  25. workbench/core/artifacts/df_store_core.py +114 -0
  26. workbench/core/artifacts/endpoint_core.py +382 -253
  27. workbench/core/artifacts/feature_set_core.py +249 -45
  28. workbench/core/artifacts/model_core.py +135 -80
  29. workbench/core/artifacts/monitor_core.py +33 -248
  30. workbench/core/artifacts/parameter_store_core.py +98 -0
  31. workbench/core/cloud_platform/aws/aws_account_clamp.py +50 -1
  32. workbench/core/cloud_platform/aws/aws_meta.py +12 -5
  33. workbench/core/cloud_platform/aws/aws_session.py +4 -4
  34. workbench/core/pipelines/pipeline_executor.py +1 -1
  35. workbench/core/transforms/data_to_features/light/molecular_descriptors.py +4 -4
  36. workbench/core/transforms/features_to_model/features_to_model.py +62 -40
  37. workbench/core/transforms/model_to_endpoint/model_to_endpoint.py +76 -15
  38. workbench/core/transforms/pandas_transforms/pandas_to_features.py +38 -2
  39. workbench/core/views/training_view.py +113 -42
  40. workbench/core/views/view.py +53 -3
  41. workbench/core/views/view_utils.py +4 -4
  42. workbench/model_script_utils/model_script_utils.py +339 -0
  43. workbench/model_script_utils/pytorch_utils.py +405 -0
  44. workbench/model_script_utils/uq_harness.py +278 -0
  45. workbench/model_scripts/chemprop/chemprop.template +649 -0
  46. workbench/model_scripts/chemprop/generated_model_script.py +649 -0
  47. workbench/model_scripts/chemprop/model_script_utils.py +339 -0
  48. workbench/model_scripts/chemprop/requirements.txt +3 -0
  49. workbench/model_scripts/custom_models/chem_info/fingerprints.py +175 -0
  50. workbench/model_scripts/custom_models/chem_info/mol_descriptors.py +483 -0
  51. workbench/model_scripts/custom_models/chem_info/mol_standardize.py +450 -0
  52. workbench/model_scripts/custom_models/chem_info/molecular_descriptors.py +7 -9
  53. workbench/model_scripts/custom_models/chem_info/morgan_fingerprints.py +1 -1
  54. workbench/model_scripts/custom_models/proximity/feature_space_proximity.py +194 -0
  55. workbench/model_scripts/custom_models/proximity/feature_space_proximity.template +8 -10
  56. workbench/model_scripts/custom_models/uq_models/bayesian_ridge.template +7 -8
  57. workbench/model_scripts/custom_models/uq_models/ensemble_xgb.template +20 -21
  58. workbench/model_scripts/custom_models/uq_models/feature_space_proximity.py +194 -0
  59. workbench/model_scripts/custom_models/uq_models/gaussian_process.template +5 -11
  60. workbench/model_scripts/custom_models/uq_models/ngboost.template +30 -18
  61. workbench/model_scripts/custom_models/uq_models/requirements.txt +1 -3
  62. workbench/model_scripts/ensemble_xgb/ensemble_xgb.template +15 -17
  63. workbench/model_scripts/meta_model/generated_model_script.py +209 -0
  64. workbench/model_scripts/meta_model/meta_model.template +209 -0
  65. workbench/model_scripts/pytorch_model/generated_model_script.py +444 -500
  66. workbench/model_scripts/pytorch_model/model_script_utils.py +339 -0
  67. workbench/model_scripts/pytorch_model/pytorch.template +440 -496
  68. workbench/model_scripts/pytorch_model/pytorch_utils.py +405 -0
  69. workbench/model_scripts/pytorch_model/requirements.txt +1 -1
  70. workbench/model_scripts/pytorch_model/uq_harness.py +278 -0
  71. workbench/model_scripts/scikit_learn/generated_model_script.py +7 -12
  72. workbench/model_scripts/scikit_learn/scikit_learn.template +4 -9
  73. workbench/model_scripts/script_generation.py +20 -11
  74. workbench/model_scripts/uq_models/generated_model_script.py +248 -0
  75. workbench/model_scripts/xgb_model/generated_model_script.py +372 -404
  76. workbench/model_scripts/xgb_model/model_script_utils.py +339 -0
  77. workbench/model_scripts/xgb_model/uq_harness.py +278 -0
  78. workbench/model_scripts/xgb_model/xgb_model.template +369 -401
  79. workbench/repl/workbench_shell.py +28 -19
  80. workbench/resources/open_source_api.key +1 -1
  81. workbench/scripts/endpoint_test.py +162 -0
  82. workbench/scripts/lambda_test.py +73 -0
  83. workbench/scripts/meta_model_sim.py +35 -0
  84. workbench/scripts/ml_pipeline_batch.py +137 -0
  85. workbench/scripts/ml_pipeline_sqs.py +186 -0
  86. workbench/scripts/monitor_cloud_watch.py +20 -100
  87. workbench/scripts/training_test.py +85 -0
  88. workbench/utils/aws_utils.py +4 -3
  89. workbench/utils/chem_utils/__init__.py +0 -0
  90. workbench/utils/chem_utils/fingerprints.py +175 -0
  91. workbench/utils/chem_utils/misc.py +194 -0
  92. workbench/utils/chem_utils/mol_descriptors.py +483 -0
  93. workbench/utils/chem_utils/mol_standardize.py +450 -0
  94. workbench/utils/chem_utils/mol_tagging.py +348 -0
  95. workbench/utils/chem_utils/projections.py +219 -0
  96. workbench/utils/chem_utils/salts.py +256 -0
  97. workbench/utils/chem_utils/sdf.py +292 -0
  98. workbench/utils/chem_utils/toxicity.py +250 -0
  99. workbench/utils/chem_utils/vis.py +253 -0
  100. workbench/utils/chemprop_utils.py +141 -0
  101. workbench/utils/cloudwatch_handler.py +1 -1
  102. workbench/utils/cloudwatch_utils.py +137 -0
  103. workbench/utils/config_manager.py +3 -7
  104. workbench/utils/endpoint_utils.py +5 -7
  105. workbench/utils/license_manager.py +2 -6
  106. workbench/utils/meta_model_simulator.py +499 -0
  107. workbench/utils/metrics_utils.py +256 -0
  108. workbench/utils/model_utils.py +278 -79
  109. workbench/utils/monitor_utils.py +44 -62
  110. workbench/utils/pandas_utils.py +3 -3
  111. workbench/utils/pytorch_utils.py +87 -0
  112. workbench/utils/shap_utils.py +11 -57
  113. workbench/utils/workbench_logging.py +0 -3
  114. workbench/utils/workbench_sqs.py +1 -1
  115. workbench/utils/xgboost_local_crossfold.py +267 -0
  116. workbench/utils/xgboost_model_utils.py +127 -219
  117. workbench/web_interface/components/model_plot.py +14 -2
  118. workbench/web_interface/components/plugin_unit_test.py +5 -2
  119. workbench/web_interface/components/plugins/dashboard_status.py +3 -1
  120. workbench/web_interface/components/plugins/generated_compounds.py +1 -1
  121. workbench/web_interface/components/plugins/model_details.py +38 -74
  122. workbench/web_interface/components/plugins/scatter_plot.py +6 -10
  123. {workbench-0.8.162.dist-info → workbench-0.8.220.dist-info}/METADATA +31 -9
  124. {workbench-0.8.162.dist-info → workbench-0.8.220.dist-info}/RECORD +128 -96
  125. workbench-0.8.220.dist-info/entry_points.txt +11 -0
  126. {workbench-0.8.162.dist-info → workbench-0.8.220.dist-info}/licenses/LICENSE +1 -1
  127. workbench/core/cloud_platform/aws/aws_df_store.py +0 -404
  128. workbench/core/cloud_platform/aws/aws_parameter_store.py +0 -280
  129. workbench/model_scripts/custom_models/chem_info/local_utils.py +0 -769
  130. workbench/model_scripts/custom_models/chem_info/tautomerize.py +0 -83
  131. workbench/model_scripts/custom_models/meta_endpoints/example.py +0 -53
  132. workbench/model_scripts/custom_models/proximity/generated_model_script.py +0 -138
  133. workbench/model_scripts/custom_models/proximity/proximity.py +0 -384
  134. workbench/model_scripts/custom_models/uq_models/generated_model_script.py +0 -393
  135. workbench/model_scripts/custom_models/uq_models/mapie_xgb.template +0 -203
  136. workbench/model_scripts/custom_models/uq_models/meta_uq.template +0 -273
  137. workbench/model_scripts/custom_models/uq_models/proximity.py +0 -384
  138. workbench/model_scripts/ensemble_xgb/generated_model_script.py +0 -279
  139. workbench/model_scripts/quant_regression/quant_regression.template +0 -279
  140. workbench/model_scripts/quant_regression/requirements.txt +0 -1
  141. workbench/utils/chem_utils.py +0 -1556
  142. workbench/utils/execution_environment.py +0 -211
  143. workbench/utils/fast_inference.py +0 -167
  144. workbench/utils/resource_utils.py +0 -39
  145. workbench-0.8.162.dist-info/entry_points.txt +0 -5
  146. {workbench-0.8.162.dist-info → workbench-0.8.220.dist-info}/WHEEL +0 -0
  147. {workbench-0.8.162.dist-info → workbench-0.8.220.dist-info}/top_level.txt +0 -0
@@ -41,12 +41,10 @@ class ModelDetails(PluginInterface):
41
41
  id=self.component_id,
42
42
  children=[
43
43
  html.H4(id=f"{self.component_id}-header", children="Model: Loading..."),
44
- dcc.Markdown(id=f"{self.component_id}-summary"),
44
+ dcc.Markdown(id=f"{self.component_id}-summary", dangerously_allow_html=True),
45
45
  html.H5(children="Inference Metrics", style={"marginTop": "20px"}),
46
46
  dcc.Dropdown(id=f"{self.component_id}-dropdown", className="dropdown"),
47
47
  dcc.Markdown(id=f"{self.component_id}-metrics"),
48
- html.H5(children="Cross Fold Metrics", style={"marginTop": "20px"}),
49
- dcc.Markdown(id=f"{self.component_id}-cross-metrics", dangerously_allow_html=True),
50
48
  ],
51
49
  )
52
50
 
@@ -57,7 +55,6 @@ class ModelDetails(PluginInterface):
57
55
  (f"{self.component_id}-dropdown", "options"),
58
56
  (f"{self.component_id}-dropdown", "value"),
59
57
  (f"{self.component_id}-metrics", "children"),
60
- (f"{self.component_id}-cross-metrics", "children"),
61
58
  ]
62
59
  self.signals = [(f"{self.component_id}-dropdown", "value")]
63
60
 
@@ -84,10 +81,9 @@ class ModelDetails(PluginInterface):
84
81
  # Populate the inference runs dropdown
85
82
  inference_runs, default_run = self.get_inference_runs()
86
83
  metrics = self.inference_metrics(default_run)
87
- cross_metrics = self.cross_metrics()
88
84
 
89
85
  # Return the updated property values for the plugin
90
- return [header, details, inference_runs, default_run, metrics, cross_metrics]
86
+ return [header, details, inference_runs, default_run, metrics]
91
87
 
92
88
  def register_internal_callbacks(self):
93
89
  @callback(
@@ -110,63 +106,37 @@ class ModelDetails(PluginInterface):
110
106
  Returns:
111
107
  str: A markdown string
112
108
  """
113
-
114
- # Get these fields from the model
115
- show_fields = [
116
- "health_tags",
117
- "input",
118
- "workbench_registered_endpoints",
119
- "workbench_model_type",
120
- "workbench_model_target",
121
- "workbench_model_features",
122
- "param_meta",
123
- "workbench_tags",
124
- ]
125
-
126
- # Construct the markdown string
127
109
  summary = self.current_model.summary()
128
110
  markdown = ""
129
- for key in show_fields:
130
-
131
- # Special case for the health tags
132
- if key == "health_tags":
133
- markdown += health_tag_markdown(summary.get(key, []))
134
- continue
135
-
136
- # Special case for the features
137
- if key == "workbench_model_features":
138
- value = summary.get(key, [])
139
- key = "features"
140
- value = f"({len(value)}) {', '.join(value)[:100]}..."
141
- markdown += f"**{key}:** {value} \n"
142
- continue
143
-
144
- # Special case for Parameter Store Metadata
145
- if key == "param_meta":
146
- model_name = summary["name"]
147
- meta_data = self.params.get(f"/workbench/models/{model_name}/meta", warn=False)
148
- if meta_data:
149
- markdown += dict_to_markdown(meta_data, title="Additional Metadata")
150
- continue
151
-
152
- # Special case for tags
153
- if key == "workbench_tags":
154
- tags = summary.get(key, "")
155
- markdown += tags_to_markdown(tags)
156
- continue
157
-
158
- # Get the value
159
- value = summary.get(key, "-")
160
-
161
- # If the value is a list, convert it to a comma-separated string
162
- if isinstance(value, list):
163
- value = ", ".join(value)
164
-
165
- # Chop off the "workbench_" prefix
166
- key = key.replace("workbench_", "")
167
-
168
- # Add to markdown string
169
- markdown += f"**{key}:** {value} \n"
111
+
112
+ # Health tags
113
+ markdown += health_tag_markdown(summary.get("health_tags", []))
114
+
115
+ # Simple fields
116
+ markdown += f"**input:** {summary.get('input', '-')} \n"
117
+ endpoints = ", ".join(summary.get("workbench_registered_endpoints", []))
118
+ markdown += f"**registered_endpoints:** {endpoints or '-'} \n"
119
+ markdown += f"**model_type:** {summary.get('workbench_model_type', '-')} \n"
120
+ markdown += f"**model_target:** {summary.get('workbench_model_target', '-')} \n"
121
+
122
+ # Features (truncated)
123
+ features = summary.get("workbench_model_features", [])
124
+ features_str = f"({len(features)}) {', '.join(features)[:100]}..."
125
+ markdown += f"**features:** {features_str} \n"
126
+
127
+ # Parameter Store metadata
128
+ model_name = summary["name"]
129
+ meta_data = self.params.get(f"/workbench/models/{model_name}/meta", warn=False)
130
+ if meta_data:
131
+ markdown += dict_to_markdown(meta_data, title="Additional Metadata")
132
+
133
+ # Tags
134
+ markdown += tags_to_markdown(summary.get("workbench_tags", "")) + " \n"
135
+
136
+ # Hyperparameters
137
+ hyperparams = summary.get("hyperparameters")
138
+ if hyperparams and isinstance(hyperparams, dict):
139
+ markdown += dict_to_collapsible_html(hyperparams, title="Hyperparameters", collapse_all=True)
170
140
 
171
141
  return markdown
172
142
 
@@ -223,17 +193,6 @@ class ModelDetails(PluginInterface):
223
193
  markdown += dict_to_markdown(inference_data, title="Additional Inference Metrics")
224
194
  return markdown
225
195
 
226
- def cross_metrics(self) -> str:
227
- # Get cross fold metrics if they exist
228
- model_name = self.current_model.name
229
- cross_fold_data = self.params.get(f"/workbench/models/{model_name}/inference/cross_fold", warn=False)
230
- if not cross_fold_data:
231
- return "**No Cross Fold Data**"
232
-
233
- # Convert the cross fold data to a markdown string
234
- html = dict_to_collapsible_html(cross_fold_data)
235
- return html
236
-
237
196
  def get_inference_runs(self):
238
197
  """Get the inference runs for the model
239
198
 
@@ -249,8 +208,13 @@ class ModelDetails(PluginInterface):
249
208
  if not inference_runs:
250
209
  return [], None
251
210
 
252
- # Set "auto_inference" as the default, if that doesn't exist, set the first
253
- default_inference_run = "auto_inference" if "auto_inference" in inference_runs else inference_runs[0]
211
+ # Default inference run (full_cross_fold if it exists, then auto_inference, then first)
212
+ if "full_cross_fold" in inference_runs:
213
+ default_inference_run = "full_cross_fold"
214
+ elif "auto_inference" in inference_runs:
215
+ default_inference_run = "auto_inference"
216
+ else:
217
+ default_inference_run = inference_runs[0]
254
218
 
255
219
  # Return the options for the dropdown and the selected value
256
220
  return inference_runs, default_inference_run
@@ -159,7 +159,7 @@ class ScatterPlot(PluginInterface):
159
159
  self.df = self.df.drop(columns=aws_cols, errors="ignore")
160
160
 
161
161
  # Set hover columns and custom data
162
- self.hover_columns = kwargs.get("hover_columns", self.df.columns.tolist()[:10])
162
+ self.hover_columns = kwargs.get("hover_columns", sorted(self.df.columns.tolist()[:15]))
163
163
  self.suppress_hover_display = kwargs.get("suppress_hover_display", False)
164
164
  self.custom_data = kwargs.get("custom_data", [])
165
165
 
@@ -420,22 +420,18 @@ if __name__ == "__main__":
420
420
  df = pd.DataFrame(data)
421
421
 
422
422
  # Get a UQ regressor model
423
- # from workbench.api import Endpoint, DFStore
424
- # end = Endpoint("aqsol-uq")
425
- # df = end.auto_inference()
426
- # DFStore().upsert("/workbench/models/aqsol-uq/auto_inference", df)
423
+ from workbench.api import Model
427
424
 
428
- from workbench.api import DFStore
429
-
430
- df = DFStore().get("/workbench/models/aqsol-uq/auto_inference")
425
+ model = Model("logd-reg-xgb")
426
+ df = model.get_inference_predictions("full_cross_fold")
431
427
 
432
428
  # Run the Unit Test on the Plugin
433
429
  PluginUnitTest(
434
430
  ScatterPlot,
435
431
  input_data=df,
436
432
  theme="midnight_blue",
437
- x="solubility",
433
+ x="logd",
438
434
  y="prediction",
439
- color="residuals_abs",
435
+ color="prediction_std",
440
436
  suppress_hover_display=True,
441
437
  ).run()
@@ -1,15 +1,37 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workbench
3
- Version: 0.8.162
3
+ Version: 0.8.220
4
4
  Summary: Workbench: A Dashboard and Python API for creating and deploying AWS SageMaker Model Pipelines
5
5
  Author-email: SuperCowPowers LLC <support@supercowpowers.com>
6
- License-Expression: MIT
6
+ License: MIT License
7
+
8
+ Copyright (c) 2021-2026 SuperCowPowers LLC
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
7
28
  Project-URL: Homepage, https://github.com/SuperCowPowers/workbench
8
29
  Keywords: SageMaker,Machine Learning,AWS,Python,Utilities
9
30
  Classifier: Development Status :: 4 - Beta
10
31
  Classifier: Programming Language :: Python :: 3.10
11
32
  Classifier: Programming Language :: Python :: 3.11
12
33
  Classifier: Programming Language :: Python :: 3.12
34
+ Classifier: Programming Language :: Python :: 3.13
13
35
  Classifier: Topic :: Scientific/Engineering
14
36
  Requires-Python: >=3.10
15
37
  Description-Content-Type: text/markdown
@@ -20,20 +42,21 @@ Requires-Dist: redis>=5.0.1
20
42
  Requires-Dist: numpy>=1.26.4
21
43
  Requires-Dist: pandas>=2.2.1
22
44
  Requires-Dist: awswrangler>=3.4.0
23
- Requires-Dist: sagemaker>=2.143
45
+ Requires-Dist: sagemaker<3.0,>=2.143
24
46
  Requires-Dist: cryptography>=44.0.2
25
- Requires-Dist: ipython>=9.0.0
47
+ Requires-Dist: ipython>=8.37.0
26
48
  Requires-Dist: pyreadline3; sys_platform == "win32"
27
49
  Requires-Dist: scikit-learn>=1.5.2
50
+ Requires-Dist: umap-learn>=0.5.8
28
51
  Requires-Dist: xgboost>=3.0.3
29
52
  Requires-Dist: joblib>=1.3.2
30
53
  Requires-Dist: requests>=2.26.0
31
54
  Requires-Dist: rdkit>=2024.9.5
32
55
  Requires-Dist: mordredcommunity>=2.0.6
33
- Requires-Dist: workbench-bridges>=0.1.8
56
+ Requires-Dist: workbench-bridges>=0.1.16
34
57
  Provides-Extra: ui
35
58
  Requires-Dist: plotly>=6.0.0; extra == "ui"
36
- Requires-Dist: dash>3.0.0; extra == "ui"
59
+ Requires-Dist: dash>=3.0.0; extra == "ui"
37
60
  Requires-Dist: dash-bootstrap-components>=1.6.0; extra == "ui"
38
61
  Requires-Dist: dash-bootstrap-templates>=1.3.0; extra == "ui"
39
62
  Requires-Dist: dash_ag_grid; extra == "ui"
@@ -48,8 +71,8 @@ Requires-Dist: flake8; extra == "dev"
48
71
  Requires-Dist: black; extra == "dev"
49
72
  Provides-Extra: all
50
73
  Requires-Dist: networkx>=3.2; extra == "all"
51
- Requires-Dist: plotly>=5.18.0; extra == "all"
52
- Requires-Dist: dash<3.0.0,>=2.16.1; extra == "all"
74
+ Requires-Dist: plotly>=6.0.0; extra == "all"
75
+ Requires-Dist: dash>=3.0.0; extra == "all"
53
76
  Requires-Dist: dash-bootstrap-components>=1.6.0; extra == "all"
54
77
  Requires-Dist: dash-bootstrap-templates>=1.3.0; extra == "all"
55
78
  Requires-Dist: dash_ag_grid; extra == "all"
@@ -168,7 +191,6 @@ Using Workbench will minimize the time and manpower needed to incorporate AWS ML
168
191
 
169
192
  ```
170
193
  pip install workbench # Installs Workbench with Core Dependencies
171
- pip install 'workbench[ml-tools]' # + Shap and NetworkX
172
194
  pip install 'workbench[ui]' # + Plotly/Dash
173
195
  pip install 'workbench[dev]' # + Pytest/flake8/black
174
196
  pip install 'workbench[all]' # + All the things :)