Moral88 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Moral88/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ from .regression import mean_absolute_error, mean_squared_error, r_squared
Moral88/regression.py CHANGED
@@ -1,36 +1,51 @@
1
- def mean_absolute_error(y_true, y_pred, normalize=True, threshold=None, method='mean', library='Moral88'):
1
+ import numpy as np
2
+
3
+ def validate_inputs(y_true, y_pred):
4
+ """
5
+ Validate the inputs for type and length.
6
+ """
7
+ if not isinstance(y_true, (list, tuple, np.ndarray)) or not isinstance(y_pred, (list, tuple, np.ndarray)):
8
+ raise TypeError("Both y_true and y_pred must be lists, tuples, or numpy arrays.")
9
+
10
+ y_true = np.array(y_true)
11
+ y_pred = np.array(y_pred)
12
+
13
+ if y_true.shape != y_pred.shape:
14
+ raise ValueError("Shapes of y_true and y_pred must be the same.")
15
+
16
+ if not np.issubdtype(y_true.dtype, np.number) or not np.issubdtype(y_pred.dtype, np.number):
17
+ raise TypeError("All elements in y_true and y_pred must be numeric.")
18
+
19
+ def mean_absolute_error(y_true, y_pred, normalize=True, threshold=None, method='mean', library='Moral88', flatten=True):
2
20
  """
3
- Calculate Mean Absolute Error (MAE) or variants based on method and library.
4
-
5
- Parameters:
6
- - y_true (list or array): True values (required)
7
- - y_pred (list or array): Predicted values (required)
8
- - normalize (bool): If True, normalize the result (default: True)
9
- - threshold (tuple, optional): Apply a threshold to the predictions (default: None)
10
- - method (str): Method of calculation. Options: {'mean', 'sum', 'none'}. Default: 'mean'
11
- - library (str): Library to use for calculations. Options: {'Moral88', 'sklearn', 'torch', 'tensor', 'statsmodel', 'Dask-ML', 'MLlib'}. Default: 'Moral88'.
12
-
13
- Returns:
14
- - float or list: Calculated error based on selected method and library.
21
+ Calculate Mean Absolute Error (MAE) for single or multi-dimensional data.
15
22
  """
23
+ validate_inputs(y_true, y_pred)
24
+
25
+ # y_true = np.array(y_true)
26
+ # y_pred = np.array(y_pred)
27
+
28
+ if flatten:
29
+ y_true = y_true.ravel()
30
+ y_pred = y_pred.ravel()
31
+
16
32
  if library == 'Moral88':
17
- # Original implementation
18
33
  if threshold is not None:
19
- y_pred = [min(max(pred, threshold[0]), threshold[1]) for pred in y_pred]
34
+ y_pred = np.clip(y_pred, threshold[0], threshold[1])
20
35
 
21
- absolute_errors = [abs(y_t - y_p) for y_t, y_p in zip(y_true, y_pred)]
36
+ absolute_errors = np.abs(y_true - y_pred)
22
37
 
23
38
  if method == 'mean':
24
- result = sum(absolute_errors) / len(y_true)
39
+ result = np.mean(absolute_errors)
25
40
  elif method == 'sum':
26
- result = sum(absolute_errors)
41
+ result = np.sum(absolute_errors)
27
42
  elif method == 'none':
28
43
  result = absolute_errors
29
44
  else:
30
45
  raise ValueError("Invalid method. Choose from {'mean', 'sum', 'none'}.")
31
46
 
32
47
  if normalize and method != 'none':
33
- range_y = max(y_true) - min(y_true)
48
+ range_y = np.ptp(y_true)
34
49
  result = result / max(abs(range_y), 1)
35
50
 
36
51
  return result
@@ -51,52 +66,39 @@ def mean_absolute_error(y_true, y_pred, normalize=True, threshold=None, method='
51
66
  y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
52
67
  return tf.reduce_mean(tf.abs(y_true_tensor - y_pred_tensor)).numpy()
53
68
 
54
- # elif library == 'statsmodel':
55
- # raise NotImplementedError("Statsmodel does not have a built-in MAE implementation.")
56
-
57
- # elif library == 'Dask-ML':
58
- # raise NotImplementedError("Dask-ML support is not implemented yet.")
59
-
60
- # elif library == 'MLlib':
61
- # raise NotImplementedError("MLlib support is not implemented yet.")
62
-
63
69
  else:
64
70
  raise ValueError(f"Invalid library: {library}. Choose from {'Moral88', 'sklearn', 'torch', 'tensorflow'}.")
65
71
 
66
-
67
- def mean_squared_error(y_true, y_pred, normalize=True, threshold=None, method='mean', library='Moral88'):
72
+ def mean_squared_error(y_true, y_pred, normalize=True, threshold=None, method='mean', library='Moral88', flatten=True):
68
73
  """
69
- Calculate Mean Squared Error (MSE) or variants based on method and library.
70
-
71
- Parameters:
72
- - y_true (list or array): True values (required)
73
- - y_pred (list or array): Predicted values (required)
74
- - normalize (bool): If True, normalize the result (default: True)
75
- - threshold (tuple, optional): Apply a threshold to the predictions (default: None)
76
- - method (str): Method of calculation. Options: {'mean', 'sum', 'none'}. Default: 'mean'
77
- - library (str): Library to use for calculations. Options: {'Moral88', 'sklearn', 'torch', 'tensor', 'statsmodel', 'Dask-ML', 'MLlib'}. Default: 'Moral88'.
78
-
79
- Returns:
80
- - float or list: Calculated error based on selected method and library.
74
+ Calculate Mean Squared Error (MSE) for single or multi-dimensional data.
81
75
  """
76
+ validate_inputs(y_true, y_pred)
77
+
78
+ # y_true = np.array(y_true)
79
+ # y_pred = np.array(y_pred)
80
+
81
+ if flatten:
82
+ y_true = y_true.ravel()
83
+ y_pred = y_pred.ravel()
84
+
82
85
  if library == 'Moral88':
83
- # Original implementation
84
86
  if threshold is not None:
85
- y_pred = [min(max(pred, threshold[0]), threshold[1]) for pred in y_pred]
87
+ y_pred = np.clip(y_pred, threshold[0], threshold[1])
86
88
 
87
- squared_errors = [(y_t - y_p) ** 2 for y_t, y_p in zip(y_true, y_pred)]
89
+ squared_errors = (y_true - y_pred) ** 2
88
90
 
89
91
  if method == 'mean':
90
- result = sum(squared_errors) / len(y_true)
92
+ result = np.mean(squared_errors)
91
93
  elif method == 'sum':
92
- result = sum(squared_errors)
94
+ result = np.sum(squared_errors)
93
95
  elif method == 'none':
94
96
  result = squared_errors
95
97
  else:
96
98
  raise ValueError("Invalid method. Choose from {'mean', 'sum', 'none'}.")
97
99
 
98
100
  if normalize and method != 'none':
99
- range_y = max(y_true) - min(y_true)
101
+ range_y = np.ptp(y_true)
100
102
  result = result / max(abs(range_y), 1)
101
103
 
102
104
  return result
@@ -117,23 +119,148 @@ def mean_squared_error(y_true, y_pred, normalize=True, threshold=None, method='m
117
119
  y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
118
120
  return tf.reduce_mean(tf.square(y_true_tensor - y_pred_tensor)).numpy()
119
121
 
120
- # elif library == 'statsmodel':
121
- # raise NotImplementedError("Statsmodel does not have a built-in MSE implementation.")
122
-
123
- # elif library == 'Dask-ML':
124
- # raise NotImplementedError("Dask-ML support is not implemented yet.")
125
-
126
- # elif library == 'MLlib':
127
- # raise NotImplementedError("MLlib support is not implemented yet.")
128
-
129
122
  else:
130
123
  raise ValueError(f"Invalid library: {library}. Choose from {'Moral88', 'sklearn', 'torch', 'tensorflow'}.")
131
- def r_squared(y_true, y_pred):
124
+
125
+ def r_squared(y_true, y_pred, flatten=True):
132
126
  """
133
- Compute R-Squared
127
+ Compute R-Squared for single or multi-dimensional data.
134
128
  """
135
- import numpy as np
136
- y_true, y_pred = np.array(y_true), np.array(y_pred)
129
+ validate_inputs(y_true, y_pred)
130
+
131
+ y_true = np.array(y_true)
132
+ y_pred = np.array(y_pred)
133
+
134
+ if flatten:
135
+ y_true = y_true.ravel()
136
+ y_pred = y_pred.ravel()
137
+
137
138
  ss_total = np.sum((y_true - np.mean(y_true)) ** 2)
138
139
  ss_residual = np.sum((y_true - y_pred) ** 2)
139
- return 1 - (ss_residual / ss_total)
140
+ return 1 - (ss_residual / ss_total)
141
+
142
+ import numpy as np
143
+ import warnings
144
+ from typing import Union, List, Tuple
145
+ from scipy import sparse
146
+
147
+ class DataValidator:
148
+ def __init__(self):
149
+ pass
150
+
151
+ def check_device_cpu(self, device):
152
+ if device not in {"cpu", None}:
153
+ raise ValueError(f"Unsupported device: {device!r}. Only 'cpu' is supported.")
154
+
155
+ def is_1d_array(self, array: Union[np.ndarray, list], warn: bool = False) -> np.ndarray:
156
+ """
157
+ Ensures input is a 1D array. Raises an error if it's not 1D or convertible to 1D.
158
+ """
159
+ array = np.asarray(array)
160
+ shape = array.shape
161
+
162
+ if len(shape) == 1:
163
+ return array
164
+ elif len(shape) == 2 and shape[1] == 1:
165
+ if warn:
166
+ warnings.warn("Input is 2D but will be converted to 1D.", UserWarning)
167
+ return array.ravel()
168
+ else:
169
+ raise ValueError(f"Input must be 1D. Found shape {shape}.")
170
+
171
+ def check_samples(self, array: Union[np.ndarray, list]) -> int:
172
+ """
173
+ Returns the number of samples in the array.
174
+ """
175
+ if hasattr(array, 'shape') and len(array.shape) > 0:
176
+ return array.shape[0]
177
+ else:
178
+ raise TypeError("Input must be an array-like object with at least one dimension.")
179
+
180
+ def check_consistent_length(self, *arrays: Union[np.ndarray, list]):
181
+ """
182
+ Ensures all input arrays have the same length.
183
+ """
184
+ lengths = [self.check_samples(arr) for arr in arrays]
185
+ if len(set(lengths)) > 1:
186
+ raise ValueError(f"Inconsistent lengths: {lengths}")
187
+
188
+ def validate_regression_targets(self, y_true, y_pred, dtype=np.float64):
189
+ """
190
+ Ensures regression target values are consistent and converted to the specified dtype.
191
+ """
192
+ y_true = np.asarray(y_true, dtype=dtype)
193
+ y_pred = np.asarray(y_pred, dtype=dtype)
194
+
195
+ if y_true.shape != y_pred.shape:
196
+ raise ValueError(f"Shapes of y_true {y_true.shape} and y_pred {y_pred.shape} do not match.")
197
+
198
+ return y_true, y_pred
199
+
200
+ def check_array(self, array, ensure_2d: bool = True, dtype=np.float64, allow_nan: bool = False):
201
+ """
202
+ Validates input array and converts it to specified dtype.
203
+ """
204
+ array = np.asarray(array, dtype=dtype)
205
+
206
+ if ensure_2d and array.ndim == 1:
207
+ array = array.reshape(-1, 1)
208
+
209
+ if not allow_nan and np.isnan(array).any():
210
+ raise ValueError("Input contains NaN values, which are not allowed.")
211
+
212
+ return array
213
+
214
+ def check_sparse(self, array, accept_sparse: Tuple[str] = ('csr', 'csc')):
215
+ """
216
+ Validates sparse matrices and converts to an acceptable format.
217
+ """
218
+ if sparse.issparse(array):
219
+ if array.format not in accept_sparse:
220
+ return array.asformat(accept_sparse[0])
221
+ return array
222
+ else:
223
+ raise ValueError("Input is not a sparse matrix.")
224
+
225
+ def validate_r2_score_inputs(self, y_true, y_pred, sample_weight=None):
226
+ """
227
+ Ensures inputs for R2 score computation are valid.
228
+ """
229
+ y_true, y_pred = self.validate_regression_targets(y_true, y_pred)
230
+ if sample_weight is not None:
231
+ sample_weight = self.is_1d_array(sample_weight)
232
+ return y_true, y_pred, sample_weight
233
+
234
+
235
+ class Metrics:
236
+ def __init__(self):
237
+ self.validator = DataValidator()
238
+
239
+ def r2_score(self, y_true, y_pred, sample_weight=None):
240
+ """
241
+ Computes R2 score.
242
+ """
243
+ y_true, y_pred, sample_weight = self.validator.validate_r2_score_inputs(y_true, y_pred, sample_weight)
244
+
245
+ numerator = np.sum((y_true - y_pred) ** 2)
246
+ denominator = np.sum((y_true - np.mean(y_true)) ** 2)
247
+
248
+ if denominator == 0:
249
+ return 0.0
250
+ return 1 - (numerator / denominator)
251
+
252
+
253
+ if __name__ == '__main__':
254
+ # Example usage
255
+ validator = DataValidator()
256
+ metrics = Metrics()
257
+
258
+ # Test validation
259
+ arr = [[1], [2], [3]]
260
+ print("1D array:", validator.is_1d_array(arr))
261
+ print("Samples:", validator.check_samples(arr))
262
+
263
+ # Test R2 score
264
+ y_true = [3, -0.5, 2, 7]
265
+ y_pred = [2.5, 0.0, 2, 8]
266
+ print("R2 Score:", metrics.r2_score(y_true, y_pred))
@@ -0,0 +1,38 @@
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Moral88
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: A library for regression evaluation metrics.
5
5
  Author: Morteza Alizadeh
6
6
  Author-email: alizadeh.c2m@gmail.com
@@ -0,0 +1,8 @@
1
+ Moral88/__init__.py,sha256=vb-aPc9ZbnYNSy9qq2fVESI63E10pYsCrDpnV8OHWkg,74
2
+ Moral88/regression.py,sha256=kfWQcdtdZVlHW_iIRbS9_rNrKJOYNiT9RaeYpVIvl7I,9355
3
+ Moral88/segmentation.py,sha256=v0yqxdrKbM9LM7wVKLjJ4HrhrSrilNNeWS6-oK_27Ag,1363
4
+ Moral88-0.4.0.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ Moral88-0.4.0.dist-info/METADATA,sha256=TITXx4rc91SXkAwwDXu2mW-FbV1xQukBXEFiVfgRXMY,407
6
+ Moral88-0.4.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
7
+ Moral88-0.4.0.dist-info/top_level.txt,sha256=-dyn5iTprnSUHbtMpvRO-prJsIoaRxao7wlfCHLSsv4,8
8
+ Moral88-0.4.0.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- Moral88/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- Moral88/regression.py,sha256=W_galfrJHIbyP8S1YL0_GMBCKVKfbI2uJfI1PRsAaOk,5939
3
- Moral88-0.2.0.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- Moral88-0.2.0.dist-info/METADATA,sha256=L3o3beMiux_cQtS6PE8sWQbC6Q2K4RuId_n6X7-tEtg,407
5
- Moral88-0.2.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
6
- Moral88-0.2.0.dist-info/top_level.txt,sha256=-dyn5iTprnSUHbtMpvRO-prJsIoaRxao7wlfCHLSsv4,8
7
- Moral88-0.2.0.dist-info/RECORD,,