validmind 2.0.0__py3-none-any.whl → 2.0.7__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 (69) hide show
  1. validmind/__init__.py +4 -1
  2. validmind/__version__.py +1 -1
  3. validmind/ai.py +197 -0
  4. validmind/api_client.py +16 -4
  5. validmind/client.py +23 -3
  6. validmind/datasets/classification/customer_churn.py +2 -2
  7. validmind/datasets/nlp/__init__.py +5 -0
  8. validmind/datasets/nlp/cnn_dailymail.py +98 -0
  9. validmind/datasets/nlp/datasets/cnn_dailymail_100_with_predictions.csv +255 -0
  10. validmind/datasets/nlp/datasets/cnn_dailymail_500_with_predictions.csv +1277 -0
  11. validmind/datasets/nlp/datasets/sentiments_with_predictions.csv +4847 -0
  12. validmind/errors.py +11 -1
  13. validmind/models/huggingface.py +2 -2
  14. validmind/models/pytorch.py +3 -3
  15. validmind/models/sklearn.py +4 -4
  16. validmind/tests/__init__.py +47 -9
  17. validmind/tests/data_validation/DatasetDescription.py +0 -1
  18. validmind/tests/data_validation/PiTCreditScoresHistogram.py +8 -3
  19. validmind/tests/data_validation/TargetRateBarPlots.py +3 -1
  20. validmind/tests/data_validation/nlp/StopWords.py +1 -6
  21. validmind/tests/data_validation/nlp/TextDescription.py +20 -9
  22. validmind/tests/decorator.py +189 -0
  23. validmind/tests/model_validation/MeteorScore.py +92 -0
  24. validmind/tests/model_validation/RegardHistogram.py +5 -6
  25. validmind/tests/model_validation/RegardScore.py +3 -5
  26. validmind/tests/model_validation/RougeMetrics.py +6 -4
  27. validmind/tests/model_validation/SelfCheckNLIScore.py +112 -0
  28. validmind/tests/model_validation/embeddings/DescriptiveAnalytics.py +17 -22
  29. validmind/tests/model_validation/sklearn/ClassifierPerformance.py +3 -1
  30. validmind/tests/model_validation/sklearn/SHAPGlobalImportance.py +30 -4
  31. validmind/tests/model_validation/sklearn/TrainingTestDegradation.py +9 -3
  32. validmind/tests/model_validation/statsmodels/ADF.py +27 -1
  33. validmind/tests/model_validation/statsmodels/RegressionModelsPerformance.py +1 -1
  34. validmind/tests/model_validation/statsmodels/ResidualsVisualInspection.py +1 -13
  35. validmind/tests/prompt_validation/ai_powered_test.py +2 -0
  36. validmind/unit_metrics/__init__.py +0 -2
  37. validmind/unit_metrics/composite.py +275 -0
  38. validmind/unit_metrics/regression/GiniCoefficient.py +39 -0
  39. validmind/unit_metrics/regression/HuberLoss.py +27 -0
  40. validmind/unit_metrics/regression/KolmogorovSmirnovStatistic.py +36 -0
  41. validmind/unit_metrics/regression/MeanAbsolutePercentageError.py +22 -0
  42. validmind/unit_metrics/regression/MeanBiasDeviation.py +22 -0
  43. validmind/unit_metrics/regression/QuantileLoss.py +25 -0
  44. validmind/unit_metrics/regression/sklearn/AdjustedRSquaredScore.py +27 -0
  45. validmind/unit_metrics/regression/sklearn/MeanAbsoluteError.py +22 -0
  46. validmind/unit_metrics/regression/sklearn/MeanSquaredError.py +22 -0
  47. validmind/unit_metrics/regression/sklearn/RSquaredScore.py +22 -0
  48. validmind/unit_metrics/regression/sklearn/RootMeanSquaredError.py +23 -0
  49. validmind/unit_metrics/sklearn/classification/Accuracy.py +2 -0
  50. validmind/unit_metrics/sklearn/classification/F1.py +2 -0
  51. validmind/unit_metrics/sklearn/classification/Precision.py +2 -0
  52. validmind/unit_metrics/sklearn/classification/ROC_AUC.py +2 -0
  53. validmind/unit_metrics/sklearn/classification/Recall.py +2 -0
  54. validmind/utils.py +17 -1
  55. validmind/vm_models/dataset.py +376 -21
  56. validmind/vm_models/figure.py +52 -17
  57. validmind/vm_models/test/metric.py +33 -30
  58. validmind/vm_models/test/output_template.py +0 -27
  59. validmind/vm_models/test/result_wrapper.py +57 -24
  60. validmind/vm_models/test/test.py +2 -1
  61. validmind/vm_models/test/threshold_test.py +24 -13
  62. validmind/vm_models/test_context.py +7 -0
  63. validmind/vm_models/test_suite/runner.py +1 -1
  64. validmind/vm_models/test_suite/test.py +1 -1
  65. {validmind-2.0.0.dist-info → validmind-2.0.7.dist-info}/METADATA +9 -13
  66. {validmind-2.0.0.dist-info → validmind-2.0.7.dist-info}/RECORD +69 -48
  67. validmind-2.0.7.dist-info/entry_points.txt +3 -0
  68. {validmind-2.0.0.dist-info → validmind-2.0.7.dist-info}/LICENSE +0 -0
  69. {validmind-2.0.0.dist-info → validmind-2.0.7.dist-info}/WHEEL +0 -0
@@ -0,0 +1,275 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ import ast
6
+ import inspect
7
+ from dataclasses import dataclass
8
+ from typing import List
9
+ from uuid import uuid4
10
+
11
+ from ..errors import LoadTestError
12
+ from ..logging import get_logger
13
+ from ..utils import clean_docstring, run_async, test_id_to_name
14
+ from ..vm_models.test.metric import Metric
15
+ from ..vm_models.test.metric_result import MetricResult
16
+ from ..vm_models.test.result_summary import ResultSummary, ResultTable
17
+ from ..vm_models.test.result_wrapper import MetricResultWrapper
18
+ from . import _get_metric_class, run_metric
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ def _extract_class_methods(cls):
24
+ source = inspect.getsource(cls)
25
+ tree = ast.parse(source)
26
+
27
+ class MethodVisitor(ast.NodeVisitor):
28
+ def __init__(self):
29
+ self.methods = {}
30
+
31
+ def visit_FunctionDef(self, node):
32
+ self.methods[node.name] = node
33
+ self.generic_visit(node)
34
+
35
+ visitor = MethodVisitor()
36
+ visitor.visit(tree)
37
+
38
+ return visitor.methods
39
+
40
+
41
+ def _extract_required_inputs(cls):
42
+ methods = _extract_class_methods(cls)
43
+
44
+ class Visitor(ast.NodeVisitor):
45
+ def __init__(self):
46
+ self.properties = set()
47
+ self.visited_methods = set()
48
+
49
+ def visit_Attribute(self, node):
50
+ if isinstance(node.value, ast.Attribute) and node.value.attr == "inputs":
51
+ self.properties.add(node.attr)
52
+
53
+ self.generic_visit(node)
54
+
55
+ def visit_Call(self, node):
56
+ if isinstance(node.func, ast.Attribute) and isinstance(
57
+ node.func.value, ast.Name
58
+ ):
59
+ if node.func.value.id == "self" and node.func.attr in methods:
60
+ method_name = node.func.attr
61
+
62
+ if method_name not in self.visited_methods:
63
+ self.visited_methods.add(method_name)
64
+ self.visit(methods[method_name])
65
+
66
+ self.generic_visit(node)
67
+
68
+ visitor = Visitor()
69
+ visitor.visit(methods["run"])
70
+
71
+ return visitor.properties
72
+
73
+
74
+ @dataclass
75
+ class CompositeMetric(Metric):
76
+ unit_metrics: List[str] = None
77
+
78
+ def __post_init__(self):
79
+ if self._unit_metrics:
80
+ self.unit_metrics = self._unit_metrics
81
+ elif self.unit_metrics is None:
82
+ raise ValueError("unit_metrics must be provided")
83
+
84
+ if hasattr(self, "_output_template") and self._output_template:
85
+ self.output_template = self._output_template
86
+
87
+ def run(self):
88
+ self.result = run_metrics(
89
+ test_id=self.test_id,
90
+ metric_ids=self.unit_metrics,
91
+ description=self.description(),
92
+ inputs=self._get_input_dict(),
93
+ params=self.params,
94
+ output_template=self.output_template,
95
+ show=False,
96
+ )
97
+
98
+ return self.result
99
+
100
+ def summary(self, result: dict):
101
+ return ResultSummary(results=[ResultTable(data=[result])])
102
+
103
+
104
+ def load_composite_metric(
105
+ test_id: str = None,
106
+ metric_name: str = None,
107
+ unit_metrics: List[str] = None,
108
+ output_template: str = None,
109
+ ) -> CompositeMetric:
110
+ # this function can either create a composite metric from a list of unit metrics or
111
+ # load a stored composite metric based on the test id
112
+
113
+ # TODO: figure out this circular import thing:
114
+ from ..api_client import get_metadata
115
+
116
+ if test_id:
117
+ # get the unit metric ids and output template (if any) from the metadata
118
+ try:
119
+ unit_metrics = run_async(
120
+ get_metadata, f"composite_metric_def:{test_id}:unit_metrics"
121
+ )["json"]
122
+ output_template = run_async(
123
+ get_metadata, f"composite_metric_def:{test_id}:output_template"
124
+ )["json"]["output_template"]
125
+ except Exception:
126
+ logger.error(f"Could not load composite metric {test_id}")
127
+ raise LoadTestError(f"Could not load composite metric {test_id}")
128
+
129
+ description = f"""
130
+ Composite metric built from the following unit metrics:
131
+ {', '.join([metric_id.split('.')[-1] for metric_id in unit_metrics])}
132
+ """
133
+
134
+ class_def = type(
135
+ test_id.split(".")[-1] if test_id else metric_name,
136
+ (CompositeMetric,),
137
+ {
138
+ "__doc__": description,
139
+ "_unit_metrics": unit_metrics,
140
+ "_output_template": output_template,
141
+ },
142
+ )
143
+
144
+ required_inputs = set()
145
+ for metric_id in unit_metrics:
146
+ metric_cls = _get_metric_class(metric_id)
147
+ # required_inputs.update(_extract_required_inputs(metric_cls))
148
+ required_inputs.update(metric_cls.required_inputs or [])
149
+
150
+ class_def.required_inputs = list(required_inputs)
151
+
152
+ return class_def
153
+
154
+
155
+ def run_metrics(
156
+ name: str = None,
157
+ metric_ids: List[str] = None,
158
+ description: str = None,
159
+ output_template: str = None,
160
+ inputs: dict = None,
161
+ params: dict = None,
162
+ test_id: str = None,
163
+ show: bool = True,
164
+ ) -> MetricResultWrapper:
165
+ """Run a composite metric
166
+
167
+ Composite metrics are metrics that are composed of multiple unit metrics. This
168
+ works by running individual unit metrics and then combining the results into a
169
+ single "MetricResult" object that can be logged and displayed just like any other
170
+ metric result. The special thing about composite metrics is that when they are
171
+ logged to the platform, metadata describing the unit metrics and output template
172
+ used to generate the composite metric is also logged. This means that by grabbing
173
+ the metadata for a composite metric (identified by the test ID
174
+ `validmind.composite_metric.<name>`) the framework can rebuild and rerun it at
175
+ any time.
176
+
177
+ Args:
178
+ name (str, optional): Name of the composite metric. Required if test_id is not
179
+ provided. Defaults to None.
180
+ metric_ids (list[str]): List of unit metric IDs to run. Required.
181
+ description (str, optional): Description of the composite metric. Defaults to
182
+ None.
183
+ output_template (_type_, optional): Output template to customize the result
184
+ table.
185
+ inputs (_type_, optional): Inputs to pass to the unit metrics. Defaults to None
186
+ params (_type_, optional): Parameters to pass to the unit metrics. Defaults to
187
+ None.
188
+ test_id (str, optional): Test ID of the composite metric. Required if name is
189
+ not provided. Defaults to None.
190
+ show (bool, optional): Whether to show the result immediately. Defaults to True
191
+
192
+ Raises:
193
+ ValueError: If metric_ids is not provided
194
+ ValueError: If name or key is not provided
195
+
196
+ Returns:
197
+ MetricResultWrapper: The result wrapper object
198
+ """
199
+ if not metric_ids:
200
+ raise ValueError("metric_ids must be provided")
201
+
202
+ if not name and not test_id:
203
+ raise ValueError("name or key must be provided")
204
+
205
+ # if name is provided, make sure to squash it into a camel case string
206
+ if name:
207
+ name = "".join(word[0].upper() + word[1:] for word in name.split())
208
+
209
+ results = {}
210
+
211
+ for metric_id in metric_ids:
212
+ result = run_metric(
213
+ metric_id=metric_id,
214
+ inputs=inputs,
215
+ params=params,
216
+ )
217
+ results[list(result.summary.keys())[0]] = result.value
218
+
219
+ test_id = f"validmind.composite_metric.{name}" if not test_id else test_id
220
+
221
+ if not output_template:
222
+
223
+ def row(key):
224
+ return f"""
225
+ <tr>
226
+ <td><strong>{key.upper()}</strong></td>
227
+ <td>{{{{ value['{key}'] | number }}}}</td>
228
+ </tr>
229
+ """
230
+
231
+ output_template = f"""
232
+ <h1{test_id_to_name(test_id)}</h1>
233
+ <table>
234
+ <thead>
235
+ <tr>
236
+ <th>Metric</th>
237
+ <th>Value</th>
238
+ </tr>
239
+ </thead>
240
+ <tbody>
241
+ {"".join([row(key) for key in results.keys()])}
242
+ </tbody>
243
+ </table>
244
+ """
245
+
246
+ result_wrapper = MetricResultWrapper(
247
+ result_id=test_id,
248
+ result_metadata=[
249
+ {
250
+ "content_id": f"metric_description:{test_id}",
251
+ "text": clean_docstring(description),
252
+ },
253
+ {
254
+ "content_id": f"composite_metric_def:{test_id}:unit_metrics",
255
+ "json": metric_ids,
256
+ },
257
+ {
258
+ "content_id": f"composite_metric_def:{test_id}:output_template",
259
+ "json": {"output_template": output_template},
260
+ },
261
+ ],
262
+ inputs=list(inputs.keys()),
263
+ output_template=output_template,
264
+ metric=MetricResult(
265
+ key=test_id,
266
+ ref_id=str(uuid4()),
267
+ value=results,
268
+ summary=ResultSummary(results=[ResultTable(data=[results])]),
269
+ ),
270
+ )
271
+
272
+ if show:
273
+ result_wrapper.show()
274
+
275
+ return result_wrapper
@@ -0,0 +1,39 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import numpy as np
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class GiniCoefficient(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ # Sort true values and corresponding predicted values
21
+ idx = np.argsort(y_true)
22
+ y_true_sorted = y_true[idx]
23
+ y_pred_sorted = y_pred[idx]
24
+
25
+ # Compute cumulative sums
26
+ cumsum_true = np.cumsum(y_true_sorted)
27
+ cumsum_pred = np.cumsum(y_pred_sorted)
28
+
29
+ # Normalize cumulative sums
30
+ cumsum_true_norm = cumsum_true / np.max(cumsum_true)
31
+ cumsum_pred_norm = cumsum_pred / np.max(cumsum_pred)
32
+
33
+ # Compute area under the Lorenz curve
34
+ area_lorenz = np.trapz(cumsum_pred_norm, x=cumsum_true_norm)
35
+
36
+ # Compute Gini coefficient
37
+ gini_coeff = 1 - 2 * area_lorenz
38
+
39
+ return self.cache_results(metric_value=gini_coeff)
@@ -0,0 +1,27 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import numpy as np
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class HuberLoss(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ # delta - Threshold for the squared error to be linear or quadratic.
21
+ delta = 1.0
22
+ error = y_true - y_pred
23
+ quadratic_part = np.minimum(np.abs(error), delta)
24
+ linear_part = np.abs(error) - quadratic_part
25
+ value = np.mean(0.5 * quadratic_part**2 + delta * linear_part)
26
+
27
+ return self.cache_results(metric_value=value)
@@ -0,0 +1,36 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import numpy as np
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class KolmogorovSmirnovStatistic(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y.flatten()
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ # Sort true values and corresponding predicted values
21
+ idx_true = np.argsort(y_true)
22
+ idx_pred = np.argsort(y_pred)
23
+ y_true_sorted = y_true[idx_true]
24
+ y_pred_sorted = y_pred[idx_pred]
25
+
26
+ # Compute cumulative distribution functions (CDFs)
27
+ cdf_true = np.arange(1, len(y_true_sorted) + 1) / len(y_true_sorted)
28
+ cdf_pred = np.arange(1, len(y_pred_sorted) + 1) / len(y_pred_sorted)
29
+
30
+ # Compute absolute differences between CDFs
31
+ diff_cdf = np.abs(cdf_true - cdf_pred)
32
+
33
+ # Find maximum absolute difference
34
+ ks_statistic = np.max(diff_cdf)
35
+
36
+ return self.cache_results(metric_value=ks_statistic)
@@ -0,0 +1,22 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import numpy as np
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class MeanAbsolutePercentageError(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ value = np.mean(np.abs((y_true - y_pred) / y_true)) * 100
21
+
22
+ return self.cache_results(metric_value=value)
@@ -0,0 +1,22 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import numpy as np
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class MeanBiasDeviation(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ value = np.mean(y_pred - y_true)
21
+
22
+ return self.cache_results(metric_value=value)
@@ -0,0 +1,25 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import numpy as np
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class QuantileLoss(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ error = y_true - y_pred
21
+ # Quantile value (between 0 and 1).
22
+ quantile = 0.5
23
+ value = np.mean(np.maximum(quantile * error, (quantile - 1) * error))
24
+
25
+ return self.cache_results(metric_value=value)
@@ -0,0 +1,27 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import sklearn.metrics as metrics
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class AdjustedRSquaredScore(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ X_columns = self.inputs.dataset.get_features_columns()
21
+ row_count = len(y_true)
22
+ feature_count = len(X_columns)
23
+ value = 1 - (1 - metrics.r2_score(y_true, y_pred)) * (row_count - 1) / (
24
+ row_count - feature_count
25
+ )
26
+
27
+ return self.cache_results(metric_value=value)
@@ -0,0 +1,22 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from sklearn.metrics import mean_absolute_error
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class MeanAbsoluteError(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ value = mean_absolute_error(y_true, y_pred, **self.params)
21
+
22
+ return self.cache_results(metric_value=value)
@@ -0,0 +1,22 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from sklearn.metrics import mean_squared_error
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class MeanSquaredError(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ value = mean_squared_error(y_true, y_pred, **self.params)
21
+
22
+ return self.cache_results(metric_value=value)
@@ -0,0 +1,22 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import sklearn.metrics as metrics
8
+
9
+ from validmind.vm_models import UnitMetric
10
+
11
+
12
+ @dataclass
13
+ class RSquaredScore(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
16
+ def run(self):
17
+ y_true = self.inputs.dataset.y
18
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
19
+
20
+ value = metrics.r2_score(y_true, y_pred)
21
+
22
+ return self.cache_results(metric_value=value)
@@ -0,0 +1,23 @@
1
+ # Copyright © 2023-2024 ValidMind Inc. All rights reserved.
2
+ # See the LICENSE file in the root of this repository for details.
3
+ # SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import numpy as np
8
+ from sklearn.metrics import mean_squared_error
9
+
10
+ from validmind.vm_models import UnitMetric
11
+
12
+
13
+ @dataclass
14
+ class RootMeanSquaredError(UnitMetric):
15
+ required_inputs = ["dataset", "model"]
16
+
17
+ def run(self):
18
+ y_true = self.inputs.dataset.y
19
+ y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
20
+
21
+ value = np.sqrt(mean_squared_error(y_true, y_pred, **self.params))
22
+
23
+ return self.cache_results(metric_value=value)
@@ -11,6 +11,8 @@ from validmind.vm_models import UnitMetric
11
11
 
12
12
  @dataclass
13
13
  class Accuracy(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
14
16
  def run(self):
15
17
  y_true = self.inputs.dataset.y
16
18
  y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
@@ -11,6 +11,8 @@ from validmind.vm_models import UnitMetric
11
11
 
12
12
  @dataclass
13
13
  class F1(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
14
16
  def run(self):
15
17
  y_true = self.inputs.dataset.y
16
18
  y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
@@ -11,6 +11,8 @@ from validmind.vm_models import UnitMetric
11
11
 
12
12
  @dataclass
13
13
  class Precision(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
14
16
  def run(self):
15
17
  y_true = self.inputs.dataset.y
16
18
  y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
@@ -11,6 +11,8 @@ from validmind.vm_models import UnitMetric
11
11
 
12
12
  @dataclass
13
13
  class ROC_AUC(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
14
16
  def run(self):
15
17
  y_true = self.inputs.dataset.y
16
18
  y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
@@ -11,6 +11,8 @@ from validmind.vm_models import UnitMetric
11
11
 
12
12
  @dataclass
13
13
  class Recall(UnitMetric):
14
+ required_inputs = ["dataset", "model"]
15
+
14
16
  def run(self):
15
17
  y_true = self.inputs.dataset.y
16
18
  y_pred = self.inputs.dataset.y_pred(model_id=self.inputs.model.input_id)
validmind/utils.py CHANGED
@@ -85,6 +85,8 @@ def nan_to_none(obj):
85
85
 
86
86
  class NumpyEncoder(json.JSONEncoder):
87
87
  def default(self, obj):
88
+ if isinstance(obj, pd.Interval):
89
+ return f"[{obj.left}, {obj.right}]"
88
90
  if isinstance(obj, np.integer):
89
91
  return int(obj)
90
92
  if isinstance(obj, np.floating):
@@ -253,7 +255,21 @@ def clean_docstring(docstring: str) -> str:
253
255
  # Join paragraphs with double newlines for markdown
254
256
  description = "\n\n".join(paragraphs)
255
257
 
256
- return description
258
+ lines = description.split("\n")
259
+ in_bullet_list = False
260
+ for i, line in enumerate([line for line in lines]):
261
+ if line.strip().startswith("-") and not in_bullet_list:
262
+ if lines[i - 1] != "":
263
+ lines[i] = "\n" + line
264
+
265
+ in_bullet_list = True
266
+ continue
267
+ elif line.strip().startswith("-") and in_bullet_list:
268
+ continue
269
+ elif line.strip() == "" and in_bullet_list:
270
+ in_bullet_list = False
271
+
272
+ return "\n".join(lines)
257
273
 
258
274
 
259
275
  def format_number(number):