Moral88 0.8.0__tar.gz → 0.10.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ from .regression import mean_absolute_error, mean_squared_error, r2_score, mean_bias_deviation, adjusted_r2_score, root_mean_squared_error, mean_absolute_percentage_error, explained_variance_score
@@ -0,0 +1,314 @@
1
+ import numpy as np
2
+ from Moral88.utils import DataValidator
3
+
4
+ validator = DataValidator()
5
+
6
+ def mean_bias_deviation( y_true, y_pred, library=None, flatten=True):
7
+ """
8
+ Computes Mean Bias Deviation (MBD).
9
+ """
10
+ y_true, y_pred = validator.validate_mae_mse_inputs(y_true, y_pred, library)
11
+
12
+ if flatten and y_true.ndim > 1:
13
+ y_true = y_true.flatten()
14
+ y_pred = y_pred.flatten()
15
+
16
+ if library == 'sklearn':
17
+ # Sklearn does not have a direct implementation for MBD
18
+ raise NotImplementedError("Mean Bias Deviation is not implemented in sklearn.")
19
+
20
+ if library == 'torch':
21
+ import torch
22
+ y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
23
+ y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
24
+ bias = torch.mean(y_pred_tensor - y_true_tensor).item()
25
+ return bias
26
+
27
+ if library == 'tensorflow':
28
+ import tensorflow as tf
29
+ y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
30
+ y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
31
+ bias = tf.reduce_mean(y_pred_tensor - y_true_tensor).numpy()
32
+ return bias
33
+
34
+ # Default implementation
35
+ return np.mean(y_pred - y_true)
36
+
37
+ def r2_score( y_true, y_pred, sample_weight=None, library=None, flatten=True):
38
+ """
39
+ Computes R2 score.
40
+ """
41
+ y_true, y_pred, sample_weight = validator.validate_r2_score_inputs(y_true, y_pred, sample_weight)
42
+
43
+ if flatten and y_true.ndim > 1:
44
+ y_true = y_true.flatten()
45
+ y_pred = y_pred.flatten()
46
+
47
+ if library == 'sklearn':
48
+ from sklearn.metrics import r2_score as sklearn_r2
49
+ return sklearn_r2(y_true, y_pred, sample_weight=sample_weight)
50
+
51
+ if library == 'statsmodels':
52
+ import statsmodels.api as sm
53
+ model = sm.OLS(y_true, sm.add_constant(y_pred)).fit()
54
+ return model.rsquared
55
+
56
+ numerator = np.sum((y_true - y_pred) ** 2)
57
+ denominator = np.sum((y_true - np.mean(y_true)) ** 2)
58
+
59
+ if denominator == 0:
60
+ return 0.0
61
+ return 1 - (numerator / denominator)
62
+
63
+ def mean_absolute_error( y_true, y_pred, normalize=True, threshold=None, method='mean', library='Moral88', flatten=True):
64
+ """
65
+ Computes Mean Absolute Error (MAE).
66
+ """
67
+ y_true, y_pred = validator.validate_mae_mse_inputs(y_true, y_pred, library)
68
+
69
+ if flatten:
70
+ y_true = y_true.ravel()
71
+ y_pred = y_pred.ravel()
72
+
73
+ if library == 'Moral88':
74
+ if threshold is not None:
75
+ y_pred = np.clip(y_pred, threshold[0], threshold[1])
76
+
77
+ if y_true.ndim > 1 and flatten:
78
+ y_true = y_true.flatten()
79
+ y_pred = y_pred.flatten()
80
+ absolute_errors = np.abs(y_true - y_pred)
81
+
82
+ if method == 'mean':
83
+ result = np.mean(absolute_errors)
84
+ elif method == 'sum':
85
+ result = np.sum(absolute_errors)
86
+ elif method == 'none':
87
+ result = absolute_errors
88
+ else:
89
+ raise ValueError("Invalid method. Choose from {'mean', 'sum', 'none'}.")
90
+
91
+ # if normalize and method != 'none':
92
+ # range_y = np.ptp(y_true)
93
+ # result = result / max(abs(range_y), 1)
94
+
95
+ return result
96
+
97
+ elif library == 'sklearn':
98
+ from sklearn.metrics import mean_absolute_error as sklearn_mae
99
+ return sklearn_mae(y_true, y_pred)
100
+
101
+ elif library == 'torch':
102
+ import torch
103
+ y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
104
+ y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
105
+ return torch.mean(torch.abs(y_true_tensor - y_pred_tensor)).item()
106
+
107
+ elif library == 'tensorflow':
108
+ import tensorflow as tf
109
+ y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
110
+ y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
111
+ return tf.reduce_mean(tf.abs(y_true_tensor - y_pred_tensor)).numpy()
112
+
113
+ def mean_squared_error(y_true, y_pred, normalize=True, threshold=None, method='mean', library='Moral88', flatten=True):
114
+ """
115
+ Computes Mean Squared Error (MSE).
116
+ """
117
+ y_true, y_pred = validator.validate_mae_mse_inputs(y_true, y_pred, library)
118
+
119
+ if flatten:
120
+ y_true = y_true.ravel()
121
+ y_pred = y_pred.ravel()
122
+
123
+ if library == 'Moral88':
124
+ if threshold is not None:
125
+ y_pred = np.clip(y_pred, threshold[0], threshold[1])
126
+
127
+ if y_true.ndim > 1 and flatten:
128
+ y_true = y_true.flatten()
129
+ y_pred = y_pred.flatten()
130
+ squared_errors = (y_true - y_pred) ** 2
131
+
132
+ if method == 'mean':
133
+ result = np.mean(squared_errors)
134
+ elif method == 'sum':
135
+ result = np.sum(squared_errors)
136
+ elif method == 'none':
137
+ result = squared_errors
138
+ else:
139
+ raise ValueError("Invalid method. Choose from {'mean', 'sum', 'none'}.")
140
+
141
+ return result
142
+
143
+ elif library == 'sklearn':
144
+ from sklearn.metrics import mean_squared_error as sklearn_mse
145
+ return sklearn_mse(y_true, y_pred)
146
+
147
+ elif library == 'torch':
148
+ import torch
149
+ y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
150
+ y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
151
+ return torch.mean((y_true_tensor - y_pred_tensor) ** 2).item()
152
+
153
+ elif library == 'tensorflow':
154
+ import tensorflow as tf
155
+ y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
156
+ y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
157
+ return tf.reduce_mean(tf.square(y_true_tensor - y_pred_tensor)).numpy()
158
+
159
+ def root_mean_squared_error(y_true, y_pred, library=None):
160
+ """
161
+ Computes Root Mean Squared Error (RMSE).
162
+ """
163
+ y_true, y_pred = validator.validate_mae_mse_inputs(y_true, y_pred, library)
164
+
165
+ if library == 'sklearn':
166
+ from sklearn.metrics import mean_squared_error as sklearn_mse
167
+ return np.sqrt(sklearn_mse(y_true, y_pred))
168
+
169
+ if library == 'torch':
170
+ import torch
171
+ y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
172
+ y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
173
+ return torch.sqrt(torch.mean((y_true_tensor - y_pred_tensor) ** 2)).item()
174
+
175
+ if library == 'tensorflow':
176
+ import tensorflow as tf
177
+ y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
178
+ y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
179
+ return tf.sqrt(tf.reduce_mean(tf.square(y_true_tensor - y_pred_tensor))).numpy()
180
+
181
+ mse = mean_squared_error(y_true, y_pred)
182
+ return np.sqrt(mse)
183
+
184
+ def mean_absolute_percentage_error(y_true, y_pred, library=None):
185
+ """
186
+ Computes Mean Absolute Percentage Error (MAPE).
187
+ """
188
+ y_true, y_pred = validator.validate_regression_targets(y_true, y_pred)
189
+ y_true, y_pred = validator.validate_mae_mse_inputs(y_true, y_pred, library)
190
+ y_true = np.clip(y_true, 1e-8, None)
191
+
192
+ if library == 'sklearn':
193
+ from sklearn.metrics import mean_absolute_percentage_error as sklearn_mape
194
+ return sklearn_mape(y_true, y_pred) * 100
195
+
196
+ if library == 'torch':
197
+ import torch
198
+ y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
199
+ y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
200
+ return torch.mean(torch.abs((y_true_tensor - y_pred_tensor) / torch.clamp(y_true_tensor, min=1e-8))).item() * 100
201
+
202
+ if library == 'tensorflow':
203
+ import tensorflow as tf
204
+ y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
205
+ y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
206
+ return tf.reduce_mean(tf.abs((y_true_tensor - y_pred_tensor) / tf.clip_by_value(y_true_tensor, 1e-8, tf.float32.max))).numpy() * 100
207
+
208
+ return np.mean(np.abs((y_true - y_pred) / np.clip(np.abs(y_true), 1e-8, None))) * 100
209
+
210
+ def explained_variance_score(y_true, y_pred, library=None, flatten=True):
211
+ """
212
+ Computes Explained Variance Score.
213
+ """
214
+ y_true, y_pred = validator.validate_mae_mse_inputs(y_true, y_pred, library)
215
+
216
+ if library == 'sklearn':
217
+ from sklearn.metrics import explained_variance_score as sklearn_evs
218
+ return sklearn_evs(y_true, y_pred)
219
+
220
+ if library == 'torch':
221
+ import torch
222
+ y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
223
+ y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
224
+ variance_residual = torch.var(y_true_tensor - y_pred_tensor)
225
+ variance_y = torch.var(y_true_tensor)
226
+ return 1 - variance_residual / variance_y if variance_y != 0 else 0
227
+
228
+ if library == 'tensorflow':
229
+ import tensorflow as tf
230
+ y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
231
+ y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
232
+ variance_residual = tf.math.reduce_variance(y_true_tensor - y_pred_tensor)
233
+ variance_y = tf.math.reduce_variance(y_true_tensor)
234
+ return 1 - variance_residual / variance_y if variance_y != 0 else 0
235
+
236
+ numerator = np.var(y_true - y_pred)
237
+ denominator = np.var(y_true)
238
+ return 1 - numerator / denominator if denominator != 0 else 0
239
+
240
+ def adjusted_r2_score(y_true, y_pred, n_features, library=None, flatten=True):
241
+ """
242
+ Computes Adjusted R-Squared Score.
243
+
244
+ Parameters:
245
+ y_true: array-like of shape (n_samples,)
246
+ Ground truth (correct) target values.
247
+
248
+ y_pred: array-like of shape (n_samples,)
249
+ Estimated target values.
250
+
251
+ n_features: int
252
+ Number of independent features in the model.
253
+
254
+ library: str, optional (default=None)
255
+ Library to use for computation. Supports {'sklearn', 'statsmodels', None}.
256
+
257
+ flatten: bool, optional (default=True)
258
+ If True, flattens multidimensional arrays before computation.
259
+ """
260
+ # Validate inputs
261
+ y_true, y_pred, _ = validator.validate_r2_score_inputs(y_true, y_pred)
262
+
263
+ # Ensure inputs are 1D arrays
264
+ if y_true.ndim == 0 or y_pred.ndim == 0:
265
+ y_true = np.array([y_true])
266
+ y_pred = np.array([y_pred])
267
+
268
+ if flatten and y_true.ndim > 1:
269
+ y_true = y_true.flatten()
270
+ y_pred = y_pred.flatten()
271
+
272
+ if library == 'sklearn':
273
+ from sklearn.metrics import r2_score
274
+ r2 = r2_score(y_true, y_pred)
275
+ elif library == 'statsmodels':
276
+ import statsmodels.api as sm
277
+ X = sm.add_constant(y_pred)
278
+ model = sm.OLS(y_true, X).fit()
279
+ r2 = model.rsquared
280
+ else:
281
+ numerator = np.sum((y_true - y_pred) ** 2)
282
+ denominator = np.sum((y_true - np.mean(y_true)) ** 2)
283
+ r2 = 1 - (numerator / denominator) if denominator != 0 else 0.0
284
+
285
+ n_samples = len(y_true)
286
+ if n_samples <= n_features + 1:
287
+ raise ValueError("Number of samples must be greater than number of features plus one for adjusted R-squared computation.")
288
+
289
+ adjusted_r2 = 1 - (1 - r2) * (n_samples - 1) / (n_samples - n_features - 1)
290
+ return adjusted_r2
291
+
292
+ if __name__ == '__main__':
293
+ # Example usage
294
+ validator = DataValidator()
295
+
296
+
297
+ # Test validation
298
+ arr = [[1], [2], [3]]
299
+ print("1D array:", validator.is_1d_array(arr))
300
+ print("Samples:", validator.check_samples(arr))
301
+
302
+ # Test MAE, MSE, R2, MBD, EV, MAPE, RMSE
303
+ y_true = [3, -0.5, 2, 7]
304
+ y_pred = [2.5, 0.0, 2, 8]
305
+
306
+ print("Mean Absolute Error:", mean_absolute_error(y_true, y_pred))
307
+ print("Mean Squared Error:", mean_squared_error(y_true, y_pred))
308
+ print("R2 Score:", r2_score(y_true, y_pred))
309
+ print("Mean Bias Deviation: ", mean_bias_deviation(y_true, y_pred))
310
+ print("Explained Variance Score: ", explained_variance_score(y_true, y_pred))
311
+ print("Mean Absolute Percentage Error: ", mean_absolute_percentage_error(y_true, y_pred))
312
+ print("Root Mean Squared Error: ", root_mean_squared_error(y_true, y_pred))
313
+ print("adjusted_r2_score: ", adjusted_r2_score(y_true, y_pred, 2))
314
+
@@ -0,0 +1,101 @@
1
+ import numpy as np
2
+ import warnings
3
+ from typing import Union, List, Tuple
4
+ from scipy import sparse
5
+
6
+ class DataValidator:
7
+ def __init__(self):
8
+ pass
9
+
10
+ def check_device_cpu(self, device):
11
+ if device not in {"cpu", None}:
12
+ raise ValueError(f"Unsupported device: {device!r}. Only 'cpu' is supported.")
13
+
14
+ def is_1d_array(self, array: Union[np.ndarray, list], warn: bool = False) -> np.ndarray:
15
+ """
16
+ Ensures input is a 1D array. Raises an error if it's not 1D or convertible to 1D.
17
+ """
18
+ array = np.asarray(array)
19
+ shape = array.shape
20
+
21
+ if len(shape) == 1:
22
+ return array
23
+ elif len(shape) == 2 and shape[1] == 1:
24
+ if warn:
25
+ warnings.warn("Input is 2D but will be converted to 1D.", UserWarning)
26
+ return array.ravel()
27
+ else:
28
+ raise ValueError(f"Input must be 1D. Found shape {shape}.")
29
+
30
+ def check_samples(self, array: Union[np.ndarray, list]) -> int:
31
+ """
32
+ Returns the number of samples in the array.
33
+ """
34
+ array = np.asarray(array)
35
+ if hasattr(array, 'shape') and len(array.shape) > 0:
36
+ return array.shape[0]
37
+ else:
38
+ raise TypeError("Input must be an array-like object with at least one dimension.")
39
+
40
+ def check_consistent_length(self, *arrays: Union[np.ndarray, list]):
41
+ """
42
+ Ensures all input arrays have the same length.
43
+ """
44
+ lengths = [self.check_samples(arr) for arr in arrays]
45
+ if len(set(lengths)) > 1:
46
+ raise ValueError(f"Inconsistent lengths: {lengths}")
47
+
48
+ def validate_regression_targets(self, y_true, y_pred, dtype=np.float64):
49
+ """
50
+ Ensures regression target values are consistent and converted to the specified dtype.
51
+ """
52
+ y_true = np.asarray(y_true, dtype=dtype)
53
+ y_pred = np.asarray(y_pred, dtype=dtype)
54
+
55
+ if y_true.shape != y_pred.shape:
56
+ raise ValueError(f"Shapes of y_true {y_true.shape} and y_pred {y_pred.shape} do not match.")
57
+
58
+ return y_true, y_pred
59
+
60
+ def check_array(self, array, ensure_2d: bool = True, dtype=np.float64, allow_nan: bool = False):
61
+ """
62
+ Validates input array and converts it to specified dtype.
63
+ """
64
+ array = np.asarray(array, dtype=dtype)
65
+
66
+ if ensure_2d and array.ndim == 1:
67
+ array = array.reshape(-1, 1)
68
+
69
+ if not allow_nan and np.isnan(array).any():
70
+ raise ValueError("Input contains NaN values, which are not allowed.")
71
+
72
+ return array
73
+
74
+ def check_sparse(self, array, accept_sparse: Tuple[str] = ('csr', 'csc')):
75
+ """
76
+ Validates sparse matrices and converts to an acceptable format.
77
+ """
78
+ if sparse.issparse(array):
79
+ if array.format not in accept_sparse:
80
+ return array.asformat(accept_sparse[0])
81
+ return array
82
+ else:
83
+ raise ValueError("Input is not a sparse matrix.")
84
+
85
+ def validate_r2_score_inputs(self, y_true, y_pred, sample_weight=None):
86
+ """
87
+ Ensures inputs for R2 score computation are valid.
88
+ """
89
+ y_true, y_pred = self.validate_regression_targets(y_true, y_pred)
90
+ if sample_weight is not None:
91
+ sample_weight = self.is_1d_array(sample_weight)
92
+ return y_true, y_pred, sample_weight
93
+
94
+ def validate_mae_mse_inputs(self, y_true, y_pred, library=None):
95
+ """
96
+ Ensures inputs for MAE and MSE computation are valid.
97
+ """
98
+ y_true, y_pred = self.validate_regression_targets(y_true, y_pred)
99
+ if library not in {None, 'sklearn', 'torch', 'tensorflow', 'Moral88'}:
100
+ raise ValueError(f"Invalid library: {library}. Choose from {{'Moral88', 'sklearn', 'torch', 'tensorflow'}}.")
101
+ return y_true, y_pred
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Moral88
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  Summary: A library for regression evaluation metrics.
5
5
  Author: Morteza Alizadeh
6
6
  Author-email: alizadeh.c2m@gmail.com
@@ -3,9 +3,11 @@ README.md
3
3
  setup.py
4
4
  Moral88/__init__.py
5
5
  Moral88/regression.py
6
- Moral88/segmentation.py
6
+ Moral88/utils.py
7
7
  Moral88.egg-info/PKG-INFO
8
8
  Moral88.egg-info/SOURCES.txt
9
9
  Moral88.egg-info/dependency_links.txt
10
10
  Moral88.egg-info/requires.txt
11
- Moral88.egg-info/top_level.txt
11
+ Moral88.egg-info/top_level.txt
12
+ tests/__init__.py
13
+ tests/test_regression.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Moral88
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  Summary: A library for regression evaluation metrics.
5
5
  Author: Morteza Alizadeh
6
6
  Author-email: alizadeh.c2m@gmail.com
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='Moral88',
5
- version='0.8.0',
5
+ version='0.10.0',
6
6
  description='A library for regression evaluation metrics.',
7
7
  author='Morteza Alizadeh',
8
8
  author_email='alizadeh.c2m@gmail.com',
@@ -0,0 +1,99 @@
1
+ import pytest
2
+ import numpy as np
3
+ from Moral88.regression import (
4
+ mean_absolute_error,
5
+ mean_absolute_error,
6
+ mean_squared_error,
7
+ r2_score,
8
+ mean_bias_deviation,
9
+ adjusted_r2_score,
10
+ root_mean_squared_error,
11
+ mean_absolute_percentage_error,
12
+ explained_variance_score
13
+ )
14
+ import warnings
15
+ from Moral88.utils import DataValidator
16
+
17
+ validator = DataValidator()
18
+
19
+ def test_is_1d_array():
20
+ validator = DataValidator()
21
+ array = [[1], [2], [3]]
22
+ with warnings.catch_warnings():
23
+ warnings.simplefilter("ignore", UserWarning)
24
+ result = validator.is_1d_array(array, warn=True)
25
+ assert result.ndim == 1
26
+ assert np.array_equal(result, np.array([1, 2, 3]))
27
+
28
+ def test_check_samples():
29
+ validator = DataValidator()
30
+ array = [[1, 2], [3, 4], [5, 6]]
31
+ result = validator.check_samples(array)
32
+ assert result == 3
33
+
34
+ def test_check_consistent_length():
35
+ validator = DataValidator()
36
+ array1 = [1, 2, 3]
37
+ array2 = [4, 5, 6]
38
+ validator.check_consistent_length(array1, array2) # Should not raise an error
39
+
40
+ array3 = [7, 8]
41
+ with pytest.raises(ValueError):
42
+ validator.check_consistent_length(array1, array3)
43
+
44
+ def test_mean_absolute_error():
45
+
46
+ y_true = [3, -0.5, 2, 7]
47
+ y_pred = [2.5, 0.0, 2, 8]
48
+ result = mean_absolute_error(y_true, y_pred)
49
+ assert result == pytest.approx(0.5, rel=1e-2)
50
+
51
+ def test_mean_squared_error():
52
+
53
+ y_true = [3, -0.5, 2, 7]
54
+ y_pred = [2.5, 0.0, 2, 8]
55
+ result = mean_squared_error(y_true, y_pred)
56
+ assert result == pytest.approx(0.375, rel=1e-2)
57
+
58
+ def test_r2_score():
59
+
60
+ y_true = [3, -0.5, 2, 7]
61
+ y_pred = [2.5, 0.0, 2, 8]
62
+ result = r2_score(y_true, y_pred)
63
+ assert result == pytest.approx(0.948, rel=1e-2)
64
+
65
+ def test_mean_bias_deviation():
66
+
67
+ y_true = [3, -0.5, 2, 7]
68
+ y_pred = [2.5, 0.0, 2, 8]
69
+ result = mean_bias_deviation(y_true, y_pred)
70
+ assert result == pytest.approx(0.25, rel=1e-2)
71
+
72
+ def test_explained_variance_score():
73
+
74
+ y_true = [3, -0.5, 2, 7]
75
+ y_pred = [2.5, 0.0, 2, 8]
76
+ result = explained_variance_score(y_true, y_pred)
77
+ assert result == pytest.approx(0.957, rel=1e-2)
78
+
79
+ def test_mean_absolute_percentage_error():
80
+
81
+ y_true = [3, -0.5, 2, 7]
82
+ y_pred = [2.5, 0.0, 2, 8]
83
+ result = mean_absolute_percentage_error(y_true, y_pred)
84
+ assert result == pytest.approx(32.738095, rel=1e-2)
85
+
86
+ def test_root_mean_squared_error():
87
+
88
+ y_true = [3, -0.5, 2, 7]
89
+ y_pred = [2.5, 0.0, 2, 8]
90
+ result = root_mean_squared_error(y_true, y_pred)
91
+ assert result == pytest.approx(0.612, rel=1e-2)
92
+
93
+ def test_adjusted_r2_score():
94
+
95
+ y_true = [3, -0.5, 2, 7]
96
+ y_pred = [2.5, 0.0, 2, 8]
97
+ n_features = 2
98
+ result = adjusted_r2_score(y_true, y_pred, n_features)
99
+ assert result == pytest.approx(0.8458, rel=1e-2)
@@ -1,421 +0,0 @@
1
- import numpy as np
2
- import warnings
3
- from typing import Union, List, Tuple
4
- from scipy import sparse
5
-
6
- class DataValidator:
7
- def __init__(self):
8
- pass
9
-
10
- def check_device_cpu(self, device):
11
- if device not in {"cpu", None}:
12
- raise ValueError(f"Unsupported device: {device!r}. Only 'cpu' is supported.")
13
-
14
- def is_1d_array(self, array: Union[np.ndarray, list], warn: bool = False) -> np.ndarray:
15
- """
16
- Ensures input is a 1D array. Raises an error if it's not 1D or convertible to 1D.
17
- """
18
- array = np.asarray(array)
19
- shape = array.shape
20
-
21
- if len(shape) == 1:
22
- return array
23
- elif len(shape) == 2 and shape[1] == 1:
24
- if warn:
25
- warnings.warn("Input is 2D but will be converted to 1D.", UserWarning)
26
- return array.ravel()
27
- else:
28
- raise ValueError(f"Input must be 1D. Found shape {shape}.")
29
-
30
- def check_samples(self, array: Union[np.ndarray, list]) -> int:
31
- """
32
- Returns the number of samples in the array.
33
- """
34
- array = np.asarray(array)
35
- if hasattr(array, 'shape') and len(array.shape) > 0:
36
- return array.shape[0]
37
- else:
38
- raise TypeError("Input must be an array-like object with at least one dimension.")
39
-
40
- def check_consistent_length(self, *arrays: Union[np.ndarray, list]):
41
- """
42
- Ensures all input arrays have the same length.
43
- """
44
- lengths = [self.check_samples(arr) for arr in arrays]
45
- if len(set(lengths)) > 1:
46
- raise ValueError(f"Inconsistent lengths: {lengths}")
47
-
48
- def validate_regression_targets(self, y_true, y_pred, dtype=np.float64):
49
- """
50
- Ensures regression target values are consistent and converted to the specified dtype.
51
- """
52
- y_true = np.asarray(y_true, dtype=dtype)
53
- y_pred = np.asarray(y_pred, dtype=dtype)
54
-
55
- if y_true.shape != y_pred.shape:
56
- raise ValueError(f"Shapes of y_true {y_true.shape} and y_pred {y_pred.shape} do not match.")
57
-
58
- return y_true, y_pred
59
-
60
- def check_array(self, array, ensure_2d: bool = True, dtype=np.float64, allow_nan: bool = False):
61
- """
62
- Validates input array and converts it to specified dtype.
63
- """
64
- array = np.asarray(array, dtype=dtype)
65
-
66
- if ensure_2d and array.ndim == 1:
67
- array = array.reshape(-1, 1)
68
-
69
- if not allow_nan and np.isnan(array).any():
70
- raise ValueError("Input contains NaN values, which are not allowed.")
71
-
72
- return array
73
-
74
- def check_sparse(self, array, accept_sparse: Tuple[str] = ('csr', 'csc')):
75
- """
76
- Validates sparse matrices and converts to an acceptable format.
77
- """
78
- if sparse.issparse(array):
79
- if array.format not in accept_sparse:
80
- return array.asformat(accept_sparse[0])
81
- return array
82
- else:
83
- raise ValueError("Input is not a sparse matrix.")
84
-
85
- def validate_r2_score_inputs(self, y_true, y_pred, sample_weight=None):
86
- """
87
- Ensures inputs for R2 score computation are valid.
88
- """
89
- y_true, y_pred = self.validate_regression_targets(y_true, y_pred)
90
- if sample_weight is not None:
91
- sample_weight = self.is_1d_array(sample_weight)
92
- return y_true, y_pred, sample_weight
93
-
94
- def validate_mae_mse_inputs(self, y_true, y_pred, library=None):
95
- """
96
- Ensures inputs for MAE and MSE computation are valid.
97
- """
98
- y_true, y_pred = self.validate_regression_targets(y_true, y_pred)
99
- if library not in {None, 'sklearn', 'torch', 'tensorflow', 'Moral88'}:
100
- raise ValueError(f"Invalid library: {library}. Choose from {{'Moral88', 'sklearn', 'torch', 'tensorflow'}}.")
101
- return y_true, y_pred
102
-
103
-
104
- class metrics:
105
- def __init__(self):
106
- pass
107
- def mean_bias_deviation(self, y_true, y_pred, library=None, flatten=True):
108
- """
109
- Computes Mean Bias Deviation (MBD).
110
- """
111
- y_true, y_pred = self.validator.validate_mae_mse_inputs(y_true, y_pred, library)
112
-
113
- if flatten and y_true.ndim > 1:
114
- y_true = y_true.flatten()
115
- y_pred = y_pred.flatten()
116
-
117
- if library == 'sklearn':
118
- # Sklearn does not have a direct implementation for MBD
119
- raise NotImplementedError("Mean Bias Deviation is not implemented in sklearn.")
120
-
121
- if library == 'torch':
122
- import torch
123
- y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
124
- y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
125
- bias = torch.mean(y_pred_tensor - y_true_tensor).item()
126
- return bias
127
-
128
- if library == 'tensorflow':
129
- import tensorflow as tf
130
- y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
131
- y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
132
- bias = tf.reduce_mean(y_pred_tensor - y_true_tensor).numpy()
133
- return bias
134
-
135
- # Default implementation
136
- return np.mean(y_pred - y_true)
137
- def __init__(self):
138
- self.validator = DataValidator()
139
-
140
- def r2_score(self, y_true, y_pred, sample_weight=None, library=None, flatten=True):
141
- """
142
- Computes R2 score.
143
- """
144
- y_true, y_pred, sample_weight = self.validator.validate_r2_score_inputs(y_true, y_pred, sample_weight)
145
-
146
- if flatten and y_true.ndim > 1:
147
- y_true = y_true.flatten()
148
- y_pred = y_pred.flatten()
149
-
150
- if library == 'sklearn':
151
- from sklearn.metrics import r2_score as sklearn_r2
152
- return sklearn_r2(y_true, y_pred, sample_weight=sample_weight)
153
-
154
- if library == 'statsmodels':
155
- import statsmodels.api as sm
156
- model = sm.OLS(y_true, sm.add_constant(y_pred)).fit()
157
- return model.rsquared
158
-
159
- numerator = np.sum((y_true - y_pred) ** 2)
160
- denominator = np.sum((y_true - np.mean(y_true)) ** 2)
161
-
162
- if denominator == 0:
163
- return 0.0
164
- return 1 - (numerator / denominator)
165
-
166
- def mean_absolute_error(self, y_true, y_pred, normalize=True, threshold=None, method='mean', library='Moral88', flatten=True):
167
- """
168
- Computes Mean Absolute Error (MAE).
169
- """
170
- y_true, y_pred = self.validator.validate_mae_mse_inputs(y_true, y_pred, library)
171
-
172
- if flatten:
173
- y_true = y_true.ravel()
174
- y_pred = y_pred.ravel()
175
-
176
- if library == 'Moral88':
177
- if threshold is not None:
178
- y_pred = np.clip(y_pred, threshold[0], threshold[1])
179
-
180
- if y_true.ndim > 1 and flatten:
181
- y_true = y_true.flatten()
182
- y_pred = y_pred.flatten()
183
- absolute_errors = np.abs(y_true - y_pred)
184
-
185
- if method == 'mean':
186
- result = np.mean(absolute_errors)
187
- elif method == 'sum':
188
- result = np.sum(absolute_errors)
189
- elif method == 'none':
190
- result = absolute_errors
191
- else:
192
- raise ValueError("Invalid method. Choose from {'mean', 'sum', 'none'}.")
193
-
194
- # if normalize and method != 'none':
195
- # range_y = np.ptp(y_true)
196
- # result = result / max(abs(range_y), 1)
197
-
198
- return result
199
-
200
- elif library == 'sklearn':
201
- from sklearn.metrics import mean_absolute_error as sklearn_mae
202
- return sklearn_mae(y_true, y_pred)
203
-
204
- elif library == 'torch':
205
- import torch
206
- y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
207
- y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
208
- return torch.mean(torch.abs(y_true_tensor - y_pred_tensor)).item()
209
-
210
- elif library == 'tensorflow':
211
- import tensorflow as tf
212
- y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
213
- y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
214
- return tf.reduce_mean(tf.abs(y_true_tensor - y_pred_tensor)).numpy()
215
-
216
- def mean_squared_error(self, y_true, y_pred, normalize=True, threshold=None, method='mean', library='Moral88', flatten=True):
217
- """
218
- Computes Mean Squared Error (MSE).
219
- """
220
- y_true, y_pred = self.validator.validate_mae_mse_inputs(y_true, y_pred, library)
221
-
222
- if flatten:
223
- y_true = y_true.ravel()
224
- y_pred = y_pred.ravel()
225
-
226
- if library == 'Moral88':
227
- if threshold is not None:
228
- y_pred = np.clip(y_pred, threshold[0], threshold[1])
229
-
230
- if y_true.ndim > 1 and flatten:
231
- y_true = y_true.flatten()
232
- y_pred = y_pred.flatten()
233
- squared_errors = (y_true - y_pred) ** 2
234
-
235
- if method == 'mean':
236
- result = np.mean(squared_errors)
237
- elif method == 'sum':
238
- result = np.sum(squared_errors)
239
- elif method == 'none':
240
- result = squared_errors
241
- else:
242
- raise ValueError("Invalid method. Choose from {'mean', 'sum', 'none'}.")
243
-
244
- # if normalize and method != 'none':
245
- # range_y = np.ptp(y_true)
246
- # result = result / max(abs(range_y), 1)
247
-
248
- return result
249
-
250
- elif library == 'sklearn':
251
- from sklearn.metrics import mean_squared_error as sklearn_mse
252
- return sklearn_mse(y_true, y_pred)
253
-
254
- elif library == 'torch':
255
- import torch
256
- y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
257
- y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
258
- return torch.mean((y_true_tensor - y_pred_tensor) ** 2).item()
259
-
260
- elif library == 'tensorflow':
261
- import tensorflow as tf
262
- y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
263
- y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
264
- return tf.reduce_mean(tf.square(y_true_tensor - y_pred_tensor)).numpy()
265
-
266
- def root_mean_squared_error(self, y_true, y_pred, library=None):
267
- """
268
- Computes Root Mean Squared Error (RMSE).
269
- """
270
- y_true, y_pred = self.validator.validate_mae_mse_inputs(y_true, y_pred, library)
271
-
272
- if library == 'sklearn':
273
- from sklearn.metrics import mean_squared_error as sklearn_mse
274
- return np.sqrt(sklearn_mse(y_true, y_pred))
275
-
276
- if library == 'torch':
277
- import torch
278
- y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
279
- y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
280
- return torch.sqrt(torch.mean((y_true_tensor - y_pred_tensor) ** 2)).item()
281
-
282
- if library == 'tensorflow':
283
- import tensorflow as tf
284
- y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
285
- y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
286
- return tf.sqrt(tf.reduce_mean(tf.square(y_true_tensor - y_pred_tensor))).numpy()
287
-
288
- mse = self.mean_squared_error(y_true, y_pred)
289
- return np.sqrt(mse)
290
-
291
- def mean_absolute_percentage_error(self, y_true, y_pred, library=None):
292
- """
293
- Computes Mean Absolute Percentage Error (MAPE).
294
- """
295
- y_true, y_pred = self.validator.validate_regression_targets(y_true, y_pred)
296
- y_true, y_pred = self.validator.validate_mae_mse_inputs(y_true, y_pred, library)
297
- y_true = np.clip(y_true, 1e-8, None)
298
-
299
- if library == 'sklearn':
300
- from sklearn.metrics import mean_absolute_percentage_error as sklearn_mape
301
- return sklearn_mape(y_true, y_pred) * 100
302
-
303
- if library == 'torch':
304
- import torch
305
- y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
306
- y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
307
- return torch.mean(torch.abs((y_true_tensor - y_pred_tensor) / torch.clamp(y_true_tensor, min=1e-8))).item() * 100
308
-
309
- if library == 'tensorflow':
310
- import tensorflow as tf
311
- y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
312
- y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
313
- return tf.reduce_mean(tf.abs((y_true_tensor - y_pred_tensor) / tf.clip_by_value(y_true_tensor, 1e-8, tf.float32.max))).numpy() * 100
314
-
315
- return np.mean(np.abs((y_true - y_pred) / np.clip(np.abs(y_true), 1e-8, None))) * 100
316
-
317
- def explained_variance_score(self, y_true, y_pred, library=None, flatten=True):
318
- """
319
- Computes Explained Variance Score.
320
- """
321
- y_true, y_pred = self.validator.validate_mae_mse_inputs(y_true, y_pred, library)
322
-
323
- if library == 'sklearn':
324
- from sklearn.metrics import explained_variance_score as sklearn_evs
325
- return sklearn_evs(y_true, y_pred)
326
-
327
- if library == 'torch':
328
- import torch
329
- y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
330
- y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
331
- variance_residual = torch.var(y_true_tensor - y_pred_tensor)
332
- variance_y = torch.var(y_true_tensor)
333
- return 1 - variance_residual / variance_y if variance_y != 0 else 0
334
-
335
- if library == 'tensorflow':
336
- import tensorflow as tf
337
- y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
338
- y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
339
- variance_residual = tf.math.reduce_variance(y_true_tensor - y_pred_tensor)
340
- variance_y = tf.math.reduce_variance(y_true_tensor)
341
- return 1 - variance_residual / variance_y if variance_y != 0 else 0
342
-
343
- numerator = np.var(y_true - y_pred)
344
- denominator = np.var(y_true)
345
- return 1 - numerator / denominator if denominator != 0 else 0
346
-
347
- def adjusted_r2_score(self, y_true, y_pred, n_features, library=None, flatten=True):
348
- """
349
- Computes Adjusted R-Squared Score.
350
-
351
- Parameters:
352
- y_true: array-like of shape (n_samples,)
353
- Ground truth (correct) target values.
354
-
355
- y_pred: array-like of shape (n_samples,)
356
- Estimated target values.
357
-
358
- n_features: int
359
- Number of independent features in the model.
360
-
361
- library: str, optional (default=None)
362
- Library to use for computation. Supports {'sklearn', 'statsmodels', None}.
363
-
364
- flatten: bool, optional (default=True)
365
- If True, flattens multidimensional arrays before computation.
366
- """
367
- # Validate inputs
368
- y_true, y_pred, _ = self.validator.validate_r2_score_inputs(y_true, y_pred)
369
-
370
- # Ensure inputs are 1D arrays
371
- if y_true.ndim == 0 or y_pred.ndim == 0:
372
- y_true = np.array([y_true])
373
- y_pred = np.array([y_pred])
374
-
375
- if flatten and y_true.ndim > 1:
376
- y_true = y_true.flatten()
377
- y_pred = y_pred.flatten()
378
-
379
- if library == 'sklearn':
380
- from sklearn.metrics import r2_score
381
- r2 = r2_score(y_true, y_pred)
382
- elif library == 'statsmodels':
383
- import statsmodels.api as sm
384
- X = sm.add_constant(y_pred)
385
- model = sm.OLS(y_true, X).fit()
386
- r2 = model.rsquared
387
- else:
388
- numerator = np.sum((y_true - y_pred) ** 2)
389
- denominator = np.sum((y_true - np.mean(y_true)) ** 2)
390
- r2 = 1 - (numerator / denominator) if denominator != 0 else 0.0
391
-
392
- n_samples = len(y_true)
393
- if n_samples <= n_features + 1:
394
- raise ValueError("Number of samples must be greater than number of features plus one for adjusted R-squared computation.")
395
-
396
- adjusted_r2 = 1 - (1 - r2) * (n_samples - 1) / (n_samples - n_features - 1)
397
- return adjusted_r2
398
-
399
- if __name__ == '__main__':
400
- # Example usage
401
- validator = DataValidator()
402
- metrics = Metrics()
403
-
404
- # Test validation
405
- arr = [[1], [2], [3]]
406
- print("1D array:", validator.is_1d_array(arr))
407
- print("Samples:", validator.check_samples(arr))
408
-
409
- # Test MAE, MSE, R2, MBD, EV, MAPE, RMSE
410
- y_true = [3, -0.5, 2, 7]
411
- y_pred = [2.5, 0.0, 2, 8]
412
-
413
- print("Mean Absolute Error:", metrics.mean_absolute_error(y_true, y_pred))
414
- print("Mean Squared Error:", metrics.mean_squared_error(y_true, y_pred))
415
- print("R2 Score:", metrics.r2_score(y_true, y_pred))
416
- print("Mean Bias Deviation: ", metrics.mean_bias_deviation(y_true, y_pred))
417
- print("Explained Variance Score: ", metrics.explained_variance_score(y_true, y_pred))
418
- print("Mean Absolute Percentage Error: ", metrics.mean_absolute_percentage_error(y_true, y_pred))
419
- print("Root Mean Squared Error: ", metrics.root_mean_squared_error(y_true, y_pred))
420
- print("adjusted_r2_score: ", metrics.adjusted_r2_score(y_true, y_pred, 2))
421
-
@@ -1,38 +0,0 @@
1
- def validate_segmentation_inputs(y_true, y_pred):
2
- """
3
- Validate the inputs for type and shape.
4
- """
5
- if not isinstance(y_true, (list, tuple)) or not isinstance(y_pred, (list, tuple)):
6
- raise TypeError("Both y_true and y_pred must be lists or tuples.")
7
-
8
- if len(y_true) != len(y_pred):
9
- raise ValueError("Length of y_true and y_pred must be the same.")
10
-
11
- if not all(isinstance(x, (int, float)) for x in y_true + y_pred):
12
- raise TypeError("All elements in y_true and y_pred must be numeric.")
13
-
14
- def dice_score(y_true, y_pred, threshold=0.5):
15
- """
16
- Compute the Dice Score.
17
-
18
- Args:
19
- y_true (list or tuple): Ground truth binary values.
20
- y_pred (list or tuple): Predicted values (probabilities or binary).
21
- threshold (float): Threshold to binarize y_pred if necessary.
22
-
23
- Returns:
24
- float: Dice Score.
25
- """
26
- validate_segmentation_inputs(y_true, y_pred)
27
-
28
- # Binarize predictions based on threshold
29
- y_pred = [1 if p >= threshold else 0 for p in y_pred]
30
-
31
- # Calculate intersection and union
32
- intersection = sum(yt * yp for yt, yp in zip(y_true, y_pred))
33
- total = sum(y_true) + sum(y_pred)
34
-
35
- if total == 0:
36
- return 1.0 # Perfect match if both are completely empty
37
-
38
- return 2 * intersection / total
File without changes
File without changes
File without changes