Moral88 0.11.0__py3-none-any.whl → 0.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Moral88/classification.py +123 -0
- Moral88/clustering.py +129 -0
- Moral88/regression.py +259 -302
- Moral88/utils.py +67 -112
- {Moral88-0.11.0.dist-info → Moral88-0.13.0.dist-info}/METADATA +1 -1
- Moral88-0.13.0.dist-info/RECORD +14 -0
- {Moral88-0.11.0.dist-info → Moral88-0.13.0.dist-info}/top_level.txt +1 -1
- Test/test_classification.py +88 -0
- Test/test_clustering.py +63 -0
- Test/test_regression.py +141 -0
- Moral88/segmentation.py +0 -166
- Moral88-0.11.0.dist-info/RECORD +0 -11
- tests/test_regression.py +0 -100
- {Moral88-0.11.0.dist-info → Moral88-0.13.0.dist-info}/LICENSE +0 -0
- {Moral88-0.11.0.dist-info → Moral88-0.13.0.dist-info}/WHEEL +0 -0
- {tests → Test}/__init__.py +0 -0
Moral88/utils.py
CHANGED
@@ -1,116 +1,71 @@
|
|
1
1
|
import numpy as np
|
2
|
-
import
|
3
|
-
from typing import Union, List, Tuple
|
4
|
-
from scipy import sparse
|
2
|
+
import pandas as pd
|
5
3
|
|
6
4
|
class DataValidator:
|
7
|
-
def __init__(self):
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
5
|
+
def __init__(self, raise_warning=True):
|
6
|
+
"""Initialize the DataValidator class"""
|
7
|
+
self.raise_warning = raise_warning
|
8
|
+
|
9
|
+
def check_data_type(self, y_true, y_pred):
|
10
|
+
"""Check if input data types are valid"""
|
11
|
+
valid_types = (np.ndarray, pd.Series, pd.DataFrame, list)
|
12
|
+
if not isinstance(y_true, valid_types) or not isinstance(y_pred, valid_types):
|
13
|
+
raise TypeError("y_true and y_pred must be numpy array, pandas series, or list")
|
14
|
+
|
15
|
+
def check_missing_values(self, y_true, y_pred):
|
16
|
+
"""Check for missing values"""
|
17
|
+
if np.any(pd.isnull(y_true)) or np.any(pd.isnull(y_pred)):
|
18
|
+
raise ValueError("Missing values (NaN) detected in data")
|
19
|
+
|
20
|
+
def check_inf_values(self, y_true, y_pred):
|
21
|
+
"""Check for infinite values"""
|
22
|
+
if np.any(np.isinf(y_true)) or np.any(np.isinf(y_pred)):
|
23
|
+
raise ValueError("Infinite values (inf) detected in data")
|
24
|
+
|
25
|
+
def check_lengths(self, y_true, y_pred):
|
26
|
+
"""Check if y_true and y_pred have the same length"""
|
27
|
+
if len(y_true) != len(y_pred):
|
28
|
+
raise ValueError("y_true and y_pred must have the same length")
|
29
|
+
|
30
|
+
def check_numeric_values(self, y_true, y_pred):
|
31
|
+
"""Check if values are numeric"""
|
32
|
+
if not np.issubdtype(np.array(y_true).dtype, np.number) or not np.issubdtype(np.array(y_pred).dtype, np.number):
|
33
|
+
raise TypeError("y_true and y_pred must contain numeric values")
|
34
|
+
|
35
|
+
def check_variance(self, y_true, y_pred):
|
36
|
+
"""Check if variance of y_true is zero (can cause issues in R-squared calculation)"""
|
37
|
+
if np.var(y_true) == 0:
|
38
|
+
raise ValueError("Variance of y_true is zero. R-squared may not be meaningful")
|
39
|
+
|
40
|
+
def check_non_negative(self, y_true, y_pred):
|
41
|
+
"""Check that values are non-negative for Logarithmic Mean Squared Error"""
|
42
|
+
if np.any(y_true < -1) or np.any(y_pred < -1):
|
43
|
+
raise ValueError("y_true and y_pred must be greater than or equal to -1 for log-based metrics")
|
44
|
+
|
45
|
+
def check_multicollinearity(self, X, threshold=0.9):
|
46
|
+
"""Check for multicollinearity in input features"""
|
47
|
+
if isinstance(X, pd.DataFrame):
|
48
|
+
corr_matrix = X.corr().abs()
|
49
|
+
high_corr = (corr_matrix > threshold).sum().sum() - len(X.columns)
|
50
|
+
if high_corr > 0:
|
51
|
+
raise ValueError("High multicollinearity detected in input features")
|
27
52
|
else:
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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 validate_segmentation_inputs(self, y_true, y_pred):
|
61
|
-
"""
|
62
|
-
Ensures segmentation inputs are valid, checking dimensions and consistency.
|
63
|
-
"""
|
64
|
-
y_true = np.asarray(y_true, dtype=np.int32)
|
65
|
-
y_pred = np.asarray(y_pred, dtype=np.int32)
|
66
|
-
|
67
|
-
if y_true.shape != y_pred.shape:
|
68
|
-
raise ValueError(f"Shapes of y_true {y_true.shape} and y_pred {y_pred.shape} do not match.")
|
69
|
-
|
70
|
-
if y_true.ndim < 2 or y_pred.ndim < 2:
|
71
|
-
raise ValueError("Segmentation inputs must have at least two dimensions.")
|
72
|
-
|
73
|
-
return y_true, y_pred
|
74
|
-
|
75
|
-
def check_array(self, array, ensure_2d: bool = True, dtype=np.float64, allow_nan: bool = False):
|
76
|
-
"""
|
77
|
-
Validates input array and converts it to specified dtype.
|
78
|
-
"""
|
79
|
-
array = np.asarray(array, dtype=dtype)
|
80
|
-
|
81
|
-
if ensure_2d and array.ndim == 1:
|
82
|
-
array = array.reshape(-1, 1)
|
83
|
-
|
84
|
-
if not allow_nan and np.isnan(array).any():
|
85
|
-
raise ValueError("Input contains NaN values, which are not allowed.")
|
86
|
-
|
87
|
-
return array
|
88
|
-
|
89
|
-
def check_sparse(self, array, accept_sparse: Tuple[str] = ('csr', 'csc')):
|
90
|
-
"""
|
91
|
-
Validates sparse matrices and converts to an acceptable format.
|
92
|
-
"""
|
93
|
-
if sparse.issparse(array):
|
94
|
-
if array.format not in accept_sparse:
|
95
|
-
return array.asformat(accept_sparse[0])
|
96
|
-
return array
|
97
|
-
else:
|
98
|
-
raise ValueError("Input is not a sparse matrix.")
|
99
|
-
|
100
|
-
def validate_r2_score_inputs(self, y_true, y_pred, sample_weight=None):
|
101
|
-
"""
|
102
|
-
Ensures inputs for R2 score computation are valid.
|
103
|
-
"""
|
104
|
-
y_true, y_pred = self.validate_regression_targets(y_true, y_pred)
|
105
|
-
if sample_weight is not None:
|
106
|
-
sample_weight = self.is_1d_array(sample_weight)
|
107
|
-
return y_true, y_pred, sample_weight
|
108
|
-
|
109
|
-
def validate_mae_mse_inputs(self, y_true, y_pred, library=None):
|
110
|
-
"""
|
111
|
-
Ensures inputs for MAE and MSE computation are valid.
|
112
|
-
"""
|
113
|
-
y_true, y_pred = self.validate_regression_targets(y_true, y_pred)
|
114
|
-
if library not in {None, 'sklearn', 'torch', 'tensorflow', 'Moral88'}:
|
115
|
-
raise ValueError(f"Invalid library: {library}. Choose from {{'Moral88', 'sklearn', 'torch', 'tensorflow'}}.")
|
116
|
-
return y_true, y_pred
|
53
|
+
if self.raise_warning:
|
54
|
+
print("Warning: Multicollinearity check requires a pandas DataFrame")
|
55
|
+
|
56
|
+
def validate_all(self, y_true, y_pred, log_based=False, mape_based=False):
|
57
|
+
"""Run all validation checks"""
|
58
|
+
self.check_data_type(y_true, y_pred)
|
59
|
+
self.check_missing_values(y_true, y_pred)
|
60
|
+
self.check_inf_values(y_true, y_pred)
|
61
|
+
self.check_lengths(y_true, y_pred)
|
62
|
+
self.check_numeric_values(y_true, y_pred)
|
63
|
+
self.check_variance(y_true, y_pred)
|
64
|
+
if log_based or mape_based:
|
65
|
+
self.check_non_negative(y_true, y_pred) # Ensure non-negative values for log-based functions and MAPE
|
66
|
+
return True # Return True if all checks pass
|
67
|
+
|
68
|
+
|
69
|
+
# Example usage
|
70
|
+
if __name__ == "__main__":
|
71
|
+
pass
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Moral88/__init__.py,sha256=Z7iEZUqslxRyJU2to6iX6a5Ak1XBZxU3VT4RvOCjsEU,196
|
2
|
+
Moral88/classification.py,sha256=eVm3kvLw0G4mx5oAX9rxlv2itn6-2mNbDIcMk8-dMfY,4916
|
3
|
+
Moral88/clustering.py,sha256=vMHkv1Z8hvN4CGiEyOBGrgatAlm3itosL2IK6-wGBw8,7139
|
4
|
+
Moral88/regression.py,sha256=evdfwAA2KCQz-S6MeAKath_Lkv5jKqXZGypnf0xSOHw,10207
|
5
|
+
Moral88/utils.py,sha256=rjDCHXoqCO_X32xXBupQWKXKC9jeU9lhkIilzMG31yE,3327
|
6
|
+
Test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
Test/test_classification.py,sha256=9JSL5xZvFHMAQdD4PBYJvryFeT8eetzMS0_7SWdZcS0,2747
|
8
|
+
Test/test_clustering.py,sha256=NBcM7UnofKGGE8zMrXIAhOz4b1x4S5kudlbEXvXId9Y,2011
|
9
|
+
Test/test_regression.py,sha256=rzAk5Pja01QigunfaCyd1XCI3Xs6B3CLIvINmCpTQQA,4896
|
10
|
+
Moral88-0.13.0.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
Moral88-0.13.0.dist-info/METADATA,sha256=lOabRubXWAz3uQUwah1JcaeL0nwUJZ2BYIg9h-x6XCg,408
|
12
|
+
Moral88-0.13.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
13
|
+
Moral88-0.13.0.dist-info/top_level.txt,sha256=-fC4somdxhr26-qFmeG9jny3GcRXqXlcJz9fy2fxGQs,13
|
14
|
+
Moral88-0.13.0.dist-info/RECORD,,
|
@@ -1,2 +1,2 @@
|
|
1
1
|
Moral88
|
2
|
-
|
2
|
+
Test
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import pytest
|
2
|
+
import numpy as np
|
3
|
+
from Moral88.classification import *
|
4
|
+
import warnings
|
5
|
+
from Moral88.utils import DataValidator
|
6
|
+
validator = DataValidator()
|
7
|
+
|
8
|
+
|
9
|
+
def test_check_data_type():
|
10
|
+
y_true = [1, 2, 3]
|
11
|
+
y_pred = [1, 2, 3]
|
12
|
+
assert validator.check_data_type(y_true, y_pred) is None
|
13
|
+
with pytest.raises(TypeError):
|
14
|
+
validator.check_data_type(123, y_pred)
|
15
|
+
|
16
|
+
def test_check_inf_values():
|
17
|
+
y_true = np.array([1, 2, np.inf])
|
18
|
+
y_pred = np.array([1, 2, 3])
|
19
|
+
with pytest.raises(ValueError):
|
20
|
+
validator.check_inf_values(y_true, y_pred)
|
21
|
+
|
22
|
+
def test_check_missing_values():
|
23
|
+
y_true = np.array([1, 2, np.nan])
|
24
|
+
y_pred = np.array([1, 2, 3])
|
25
|
+
with pytest.raises(ValueError):
|
26
|
+
validator.check_missing_values(y_true, y_pred)
|
27
|
+
|
28
|
+
def test_check_lengths():
|
29
|
+
y_true = [1, 2, 3]
|
30
|
+
y_pred = [1, 2]
|
31
|
+
with pytest.raises(ValueError):
|
32
|
+
validator.check_lengths(y_true, y_pred)
|
33
|
+
|
34
|
+
def test_validate_all():
|
35
|
+
y_true = [1, 2, 3]
|
36
|
+
y_pred = [1, 2, 3]
|
37
|
+
assert validator.validate_all(y_true, y_pred) is True
|
38
|
+
with pytest.raises(TypeError):
|
39
|
+
validator.validate_all(123, y_pred)
|
40
|
+
with pytest.raises(ValueError):
|
41
|
+
validator.validate_all([1, 2, np.nan], [1, 2, 3])
|
42
|
+
with pytest.raises(ValueError):
|
43
|
+
validator.validate_all([1, 2, 3], [1, 2])
|
44
|
+
|
45
|
+
def test_accuracy():
|
46
|
+
y_true = [0, 1, 1, 0, 1]
|
47
|
+
y_pred = [0, 1, 0, 0, 1]
|
48
|
+
result = accuracy(y_true, y_pred)
|
49
|
+
assert result == pytest.approx(0.8, rel=1e-2)
|
50
|
+
|
51
|
+
def test_auc_roc():
|
52
|
+
y_true = [0, 1, 1, 0, 1]
|
53
|
+
y_probs = [0.1, 0.8, 0.4, 0.3, 0.9]
|
54
|
+
result = auc_roc(y_true, y_probs, average='macro')
|
55
|
+
assert result == pytest.approx(0.75, rel=1e-2)
|
56
|
+
|
57
|
+
def test_precision():
|
58
|
+
y_true = [0, 1, 1, 0, 1]
|
59
|
+
y_pred = [0, 1, 0, 0, 1]
|
60
|
+
result = precision(y_true, y_pred, average='macro')
|
61
|
+
assert result == pytest.approx(0.75, rel=1e-2)
|
62
|
+
|
63
|
+
def test_recall():
|
64
|
+
y_true = [0, 1, 1, 0, 1]
|
65
|
+
y_pred = [0, 1, 0, 0, 1]
|
66
|
+
result = recall(y_true, y_pred, average='macro')
|
67
|
+
assert result == pytest.approx(0.75, rel=1e-2)
|
68
|
+
|
69
|
+
def test_balanced_accuracy():
|
70
|
+
y_true = [0, 1, 1, 0, 1]
|
71
|
+
y_pred = [0, 1, 0, 0, 1]
|
72
|
+
result = balanced_accuracy(y_true, y_pred)
|
73
|
+
assert result == pytest.approx(0.75, rel=1e-2)
|
74
|
+
|
75
|
+
def test_matthews_correlation_coefficient():
|
76
|
+
y_true = [0, 1, 1, 0, 1]
|
77
|
+
y_pred = [0, 1, 0, 0, 1]
|
78
|
+
result = matthews_correlation_coefficient(y_true, y_pred)
|
79
|
+
assert result == pytest.approx(0.632, rel=1e-2)
|
80
|
+
|
81
|
+
def test_cohens_kappa():
|
82
|
+
y_true = [0, 1, 1, 0, 1]
|
83
|
+
y_pred = [0, 1, 0, 0, 1]
|
84
|
+
result = cohens_kappa(y_true, y_pred)
|
85
|
+
assert result == pytest.approx(0.5, rel=1e-2)
|
86
|
+
|
87
|
+
if __name__ == "__main__":
|
88
|
+
pytest.main()
|
Test/test_clustering.py
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
import pytest
|
2
|
+
import numpy as np
|
3
|
+
from Moral88.clustering import *
|
4
|
+
import warnings
|
5
|
+
from Moral88.utils import DataValidator
|
6
|
+
|
7
|
+
|
8
|
+
def test_adjusted_rand_index():
|
9
|
+
labels_true = [0, 0, 1, 1, 2, 2]
|
10
|
+
labels_pred = [0, 1, 1, 1, 2, 2]
|
11
|
+
result = adjusted_rand_index(labels_true, labels_pred)
|
12
|
+
assert result == pytest.approx(0.444, rel=1e-2)
|
13
|
+
|
14
|
+
def test_normalized_mutual_info():
|
15
|
+
labels_true = [0, 0, 1, 1, 2, 2]
|
16
|
+
labels_pred = [0, 1, 1, 1, 2, 2]
|
17
|
+
result = normalized_mutual_info(labels_true, labels_pred)
|
18
|
+
assert result == pytest.approx(0.557, rel=1e-2)
|
19
|
+
|
20
|
+
def test_silhouette_score():
|
21
|
+
X = np.random.rand(6, 2)
|
22
|
+
labels_pred = np.array([0, 1, 1, 1, 2, 2])
|
23
|
+
result = silhouette_score(X, labels_pred)
|
24
|
+
assert isinstance(result, float)
|
25
|
+
|
26
|
+
def test_calinski_harabasz_index():
|
27
|
+
X = np.random.rand(6, 2)
|
28
|
+
labels_pred = np.array([0, 1, 1, 1, 2, 2])
|
29
|
+
result = calinski_harabasz_index(X, labels_pred)
|
30
|
+
assert isinstance(result, float)
|
31
|
+
|
32
|
+
def test_dunn_index():
|
33
|
+
X = np.random.rand(6, 2)
|
34
|
+
labels_pred = np.array([0, 1, 1, 1, 2, 2])
|
35
|
+
result = dunn_index(X, labels_pred)
|
36
|
+
assert isinstance(result, float)
|
37
|
+
|
38
|
+
def test_inertia():
|
39
|
+
X = np.random.rand(6, 2)
|
40
|
+
labels_pred = np.array([0, 1, 1, 1, 2, 2])
|
41
|
+
result = inertia(X, labels_pred)
|
42
|
+
assert isinstance(result, float)
|
43
|
+
|
44
|
+
def test_homogeneity_score():
|
45
|
+
labels_true = [0, 0, 1, 1, 2, 2]
|
46
|
+
labels_pred = [0, 1, 1, 1, 2, 2]
|
47
|
+
result = homogeneity_score(labels_true, labels_pred)
|
48
|
+
assert isinstance(result, float)
|
49
|
+
|
50
|
+
def test_completeness_score():
|
51
|
+
labels_true = [0, 0, 1, 1, 2, 2]
|
52
|
+
labels_pred = [0, 1, 1, 1, 2, 2]
|
53
|
+
result = completeness_score(labels_true, labels_pred)
|
54
|
+
assert isinstance(result, float)
|
55
|
+
|
56
|
+
def test_davies_bouldin_index():
|
57
|
+
X = np.random.rand(6, 2)
|
58
|
+
labels_pred = np.array([0, 1, 1, 1, 2, 2])
|
59
|
+
result = davies_bouldin_index(X, labels_pred)
|
60
|
+
assert isinstance(result, float)
|
61
|
+
|
62
|
+
if __name__ == "__main__":
|
63
|
+
pytest.main()
|
Test/test_regression.py
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
import pytest
|
2
|
+
import numpy as np
|
3
|
+
from Moral88.regression import (
|
4
|
+
mean_absolute_error,
|
5
|
+
mean_squared_error,
|
6
|
+
root_mean_squared_error,
|
7
|
+
mean_bias_deviation,
|
8
|
+
r_squared,
|
9
|
+
adjusted_r_squared,
|
10
|
+
mean_absolute_percentage_error,
|
11
|
+
symmetric_mean_absolute_percentage_error,
|
12
|
+
huber_loss,
|
13
|
+
relative_squared_error,
|
14
|
+
mean_squared_log_error,
|
15
|
+
root_mean_squared_log_error,
|
16
|
+
log_cosh_loss,
|
17
|
+
explained_variance,
|
18
|
+
median_absolute_error
|
19
|
+
)
|
20
|
+
import warnings
|
21
|
+
from Moral88.utils import DataValidator
|
22
|
+
|
23
|
+
validator = DataValidator()
|
24
|
+
|
25
|
+
def test_is_1d_array():
|
26
|
+
validator = DataValidator()
|
27
|
+
array = [[1], [2], [3]]
|
28
|
+
with warnings.catch_warnings():
|
29
|
+
warnings.simplefilter("ignore", UserWarning)
|
30
|
+
result = validator.is_1d_array(array, warn=True)
|
31
|
+
assert result.ndim == 1
|
32
|
+
assert np.array_equal(result, np.array([1, 2, 3]))
|
33
|
+
|
34
|
+
def test_check_samples():
|
35
|
+
validator = DataValidator()
|
36
|
+
array = [[1, 2], [3, 4], [5, 6]]
|
37
|
+
result = validator.check_samples(array)
|
38
|
+
assert result == 3
|
39
|
+
|
40
|
+
def test_check_consistent_length():
|
41
|
+
validator = DataValidator()
|
42
|
+
array1 = [1, 2, 3]
|
43
|
+
array2 = [4, 5, 6]
|
44
|
+
validator.check_consistent_length(array1, array2) # Should not raise an error
|
45
|
+
|
46
|
+
array3 = [7, 8]
|
47
|
+
with pytest.raises(ValueError):
|
48
|
+
validator.check_consistent_length(array1, array3)
|
49
|
+
|
50
|
+
def test_mean_absolute_error():
|
51
|
+
y_true = [3, -0.5, 2, 7]
|
52
|
+
y_pred = [2.5, 0.0, 2, 8]
|
53
|
+
result = mean_absolute_error(y_true, y_pred)
|
54
|
+
assert result == pytest.approx(0.5, rel=1e-2)
|
55
|
+
|
56
|
+
def test_mean_squared_error():
|
57
|
+
y_true = [3, -0.5, 2, 7]
|
58
|
+
y_pred = [2.5, 0.0, 2, 8]
|
59
|
+
result = mean_squared_error(y_true, y_pred)
|
60
|
+
assert result == pytest.approx(0.375, rel=1e-2)
|
61
|
+
|
62
|
+
def test_root_mean_squared_error():
|
63
|
+
y_true = [3, -0.5, 2, 7]
|
64
|
+
y_pred = [2.5, 0.0, 2, 8]
|
65
|
+
result = root_mean_squared_error(y_true, y_pred)
|
66
|
+
assert result == pytest.approx(0.612, rel=1e-2)
|
67
|
+
|
68
|
+
def test_mean_bias_deviation():
|
69
|
+
y_true = [3, 5, 2, 7]
|
70
|
+
y_pred = [2.5, 5.5, 2, 8]
|
71
|
+
result = mean_bias_deviation(y_true, y_pred)
|
72
|
+
assert result == pytest.approx(-0.25, rel=1e-2)
|
73
|
+
|
74
|
+
def test_r_squared():
|
75
|
+
y_true = [3, -0.5, 2, 7]
|
76
|
+
y_pred = [2.5, 0.0, 2, 8]
|
77
|
+
result = r_squared(y_true, y_pred)
|
78
|
+
assert result == pytest.approx(0.948, rel=1e-2)
|
79
|
+
|
80
|
+
def test_adjusted_r_squared():
|
81
|
+
y_true = [3, -0.5, 2, 7]
|
82
|
+
y_pred = [2.5, 0.0, 2, 8]
|
83
|
+
result = adjusted_r_squared(y_true, y_pred, n_features=2)
|
84
|
+
assert result == pytest.approx(0.896, rel=1e-2)
|
85
|
+
|
86
|
+
def test_mean_absolute_percentage_error():
|
87
|
+
y_true = [3, -0.5, 2, 7]
|
88
|
+
y_pred = [2.5, 0.0, 2, 8]
|
89
|
+
result = mean_absolute_percentage_error(y_true, y_pred)
|
90
|
+
assert result == pytest.approx(27.77, rel=1e-2)
|
91
|
+
|
92
|
+
def test_symmetric_mean_absolute_percentage_error():
|
93
|
+
y_true = [3, -0.5, 2, 7]
|
94
|
+
y_pred = [2.5, 0.0, 2, 8]
|
95
|
+
result = symmetric_mean_absolute_percentage_error(y_true, y_pred)
|
96
|
+
assert result == pytest.approx(28.99, rel=1e-2)
|
97
|
+
|
98
|
+
def test_huber_loss():
|
99
|
+
y_true = [3, -0.5, 2, 7]
|
100
|
+
y_pred = [2.5, 0.0, 2, 8]
|
101
|
+
result = huber_loss(y_true, y_pred)
|
102
|
+
assert result == pytest.approx(0.3125, rel=1e-2)
|
103
|
+
|
104
|
+
def test_relative_squared_error():
|
105
|
+
y_true = [3, -0.5, 2, 7]
|
106
|
+
y_pred = [2.5, 0.0, 2, 8]
|
107
|
+
result = relative_squared_error(y_true, y_pred)
|
108
|
+
assert result == pytest.approx(0.052, rel=1e-2)
|
109
|
+
|
110
|
+
def test_mean_squared_log_error():
|
111
|
+
y_true = [3, 5, 2, 7]
|
112
|
+
y_pred = [2.5, 4.5, 2, 6.5]
|
113
|
+
result = mean_squared_log_error(y_true, y_pred)
|
114
|
+
assert result == pytest.approx(0.004, rel=1e-2)
|
115
|
+
|
116
|
+
def test_root_mean_squared_log_error():
|
117
|
+
y_true = [3, 5, 2, 7]
|
118
|
+
y_pred = [2.5, 4.5, 2, 6.5]
|
119
|
+
result = root_mean_squared_log_error(y_true, y_pred)
|
120
|
+
assert result == pytest.approx(0.063, rel=1e-2)
|
121
|
+
|
122
|
+
def test_log_cosh_loss():
|
123
|
+
y_true = [3, -0.5, 2, 7]
|
124
|
+
y_pred = [2.5, 0.0, 2, 8]
|
125
|
+
result = log_cosh_loss(y_true, y_pred)
|
126
|
+
assert result == pytest.approx(0.216, rel=1e-2)
|
127
|
+
|
128
|
+
def test_explained_variance():
|
129
|
+
y_true = [3, -0.5, 2, 7]
|
130
|
+
y_pred = [2.5, 0.0, 2, 8]
|
131
|
+
result = explained_variance(y_true, y_pred)
|
132
|
+
assert result == pytest.approx(0.95, rel=1e-2)
|
133
|
+
|
134
|
+
def test_median_absolute_error():
|
135
|
+
y_true = [3, -0.5, 2, 7]
|
136
|
+
y_pred = [2.5, 0.0, 2, 8]
|
137
|
+
result = median_absolute_error(y_true, y_pred)
|
138
|
+
assert result == pytest.approx(0.5, rel=1e-2)
|
139
|
+
|
140
|
+
if __name__ == "__main__":
|
141
|
+
pytest.main()
|
Moral88/segmentation.py
DELETED
@@ -1,166 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
from Moral88.utils import DataValidator
|
3
|
-
from scipy.spatial.distance import directed_hausdorff
|
4
|
-
from sklearn.metrics import f1_score as sklearn_f1_score
|
5
|
-
|
6
|
-
validator = DataValidator()
|
7
|
-
|
8
|
-
def intersection_over_union(y_true, y_pred, num_classes, library=None, flatten=True):
|
9
|
-
"""
|
10
|
-
Computes Intersection over Union (IoU).
|
11
|
-
"""
|
12
|
-
y_true, y_pred = validator.validate_segmentation_inputs(y_true, y_pred)
|
13
|
-
validator.validate_classes(y_true, num_classes)
|
14
|
-
validator.validate_classes(y_pred, num_classes)
|
15
|
-
|
16
|
-
if flatten:
|
17
|
-
y_true = y_true.ravel()
|
18
|
-
y_pred = y_pred.ravel()
|
19
|
-
|
20
|
-
if library == 'Moral88' or library is None:
|
21
|
-
iou_per_class = []
|
22
|
-
for cls in range(num_classes):
|
23
|
-
intersection = np.logical_and(y_true == cls, y_pred == cls).sum()
|
24
|
-
union = np.logical_or(y_true == cls, y_pred == cls).sum()
|
25
|
-
iou = intersection / union if union > 0 else 0
|
26
|
-
iou_per_class.append(iou)
|
27
|
-
|
28
|
-
mean_iou = np.mean(iou_per_class)
|
29
|
-
return iou_per_class, mean_iou
|
30
|
-
|
31
|
-
elif library == 'torch':
|
32
|
-
import torch
|
33
|
-
y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
|
34
|
-
y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
|
35
|
-
iou = torch.mean((y_true_tensor * y_pred_tensor).sum(dim=1) / (y_true_tensor + y_pred_tensor - y_true_tensor * y_pred_tensor).sum(dim=1))
|
36
|
-
return iou.item()
|
37
|
-
|
38
|
-
elif library == 'tensorflow':
|
39
|
-
import tensorflow as tf
|
40
|
-
intersection = tf.reduce_sum(tf.cast(y_true == y_pred, tf.float32))
|
41
|
-
union = tf.reduce_sum(tf.cast(y_true | y_pred, tf.float32))
|
42
|
-
iou = intersection / union if union > 0 else 0
|
43
|
-
return iou.numpy()
|
44
|
-
|
45
|
-
raise ValueError("Unsupported library for IoU.")
|
46
|
-
|
47
|
-
def dice_coefficient(y_true, y_pred, num_classes, library=None, flatten=True):
|
48
|
-
"""
|
49
|
-
Computes Dice Coefficient.
|
50
|
-
"""
|
51
|
-
y_true, y_pred = validator.validate_segmentation_inputs(y_true, y_pred)
|
52
|
-
validator.validate_classes(y_true, num_classes)
|
53
|
-
validator.validate_classes(y_pred, num_classes)
|
54
|
-
|
55
|
-
if flatten:
|
56
|
-
y_true = y_true.ravel()
|
57
|
-
y_pred = y_pred.ravel()
|
58
|
-
|
59
|
-
if library == 'Moral88' or library is None:
|
60
|
-
dice_per_class = []
|
61
|
-
for cls in range(num_classes):
|
62
|
-
intersection = np.logical_and(y_true == cls, y_pred == cls).sum()
|
63
|
-
total = (y_true == cls).sum() + (y_pred == cls).sum()
|
64
|
-
dice = (2 * intersection) / total if total > 0 else 0
|
65
|
-
dice_per_class.append(dice)
|
66
|
-
|
67
|
-
mean_dice = np.mean(dice_per_class)
|
68
|
-
return dice_per_class, mean_dice
|
69
|
-
|
70
|
-
elif library == 'torch':
|
71
|
-
import torch
|
72
|
-
y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
|
73
|
-
y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
|
74
|
-
intersection = torch.sum(y_true_tensor * y_pred_tensor)
|
75
|
-
total = torch.sum(y_true_tensor) + torch.sum(y_pred_tensor)
|
76
|
-
dice = (2 * intersection) / total if total > 0 else 0
|
77
|
-
return dice.item()
|
78
|
-
|
79
|
-
elif library == 'tensorflow':
|
80
|
-
import tensorflow as tf
|
81
|
-
y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
|
82
|
-
y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
|
83
|
-
intersection = tf.reduce_sum(y_true_tensor * y_pred_tensor)
|
84
|
-
total = tf.reduce_sum(y_true_tensor) + tf.reduce_sum(y_pred_tensor)
|
85
|
-
dice = (2 * intersection) / total if total > 0 else 0
|
86
|
-
return dice.numpy()
|
87
|
-
|
88
|
-
raise ValueError("Unsupported library for Dice Coefficient.")
|
89
|
-
|
90
|
-
def pixel_accuracy(y_true, y_pred, library=None):
|
91
|
-
"""
|
92
|
-
Computes Pixel Accuracy.
|
93
|
-
"""
|
94
|
-
y_true, y_pred = validator.validate_segmentation_inputs(y_true, y_pred)
|
95
|
-
|
96
|
-
if library == 'Moral88' or library is None:
|
97
|
-
correct = (y_true == y_pred).sum()
|
98
|
-
total = y_true.size
|
99
|
-
accuracy = correct / total
|
100
|
-
return accuracy
|
101
|
-
|
102
|
-
elif library == 'torch':
|
103
|
-
import torch
|
104
|
-
y_true_tensor = torch.tensor(y_true, dtype=torch.float32)
|
105
|
-
y_pred_tensor = torch.tensor(y_pred, dtype=torch.float32)
|
106
|
-
correct = torch.sum(y_true_tensor == y_pred_tensor)
|
107
|
-
total = torch.numel(y_true_tensor)
|
108
|
-
accuracy = correct / total
|
109
|
-
return accuracy.item()
|
110
|
-
|
111
|
-
elif library == 'tensorflow':
|
112
|
-
import tensorflow as tf
|
113
|
-
y_true_tensor = tf.convert_to_tensor(y_true, dtype=tf.float32)
|
114
|
-
y_pred_tensor = tf.convert_to_tensor(y_pred, dtype=tf.float32)
|
115
|
-
correct = tf.reduce_sum(tf.cast(y_true_tensor == y_pred_tensor, tf.float32))
|
116
|
-
total = tf.size(y_true_tensor, out_type=tf.float32)
|
117
|
-
accuracy = correct / total
|
118
|
-
return accuracy.numpy()
|
119
|
-
|
120
|
-
raise ValueError("Unsupported library for Pixel Accuracy.")
|
121
|
-
|
122
|
-
def hausdorff_distance(y_true, y_pred, library=None):
|
123
|
-
"""
|
124
|
-
Computes Hausdorff Distance.
|
125
|
-
"""
|
126
|
-
y_true, y_pred = validator.validate_segmentation_inputs(y_true, y_pred)
|
127
|
-
|
128
|
-
if library == 'Moral88' or library is None:
|
129
|
-
y_true_points = np.argwhere(y_true > 0)
|
130
|
-
y_pred_points = np.argwhere(y_pred > 0)
|
131
|
-
|
132
|
-
distance = max(directed_hausdorff(y_true_points, y_pred_points)[0],
|
133
|
-
directed_hausdorff(y_pred_points, y_true_points)[0])
|
134
|
-
return distance
|
135
|
-
|
136
|
-
raise ValueError("Unsupported library for Hausdorff Distance.")
|
137
|
-
|
138
|
-
def f1_score(y_true, y_pred, num_classes, library=None, flatten=True):
|
139
|
-
"""
|
140
|
-
Computes F1 Score.
|
141
|
-
"""
|
142
|
-
y_true, y_pred = validator.validate_segmentation_inputs(y_true, y_pred)
|
143
|
-
validator.validate_classes(y_true, num_classes)
|
144
|
-
validator.validate_classes(y_pred, num_classes)
|
145
|
-
|
146
|
-
if flatten:
|
147
|
-
y_true = y_true.ravel()
|
148
|
-
y_pred = y_pred.ravel()
|
149
|
-
|
150
|
-
if library == 'sklearn':
|
151
|
-
return sklearn_f1_score(y_true, y_pred, average='macro')
|
152
|
-
|
153
|
-
if library == 'Moral88' or library is None:
|
154
|
-
f1_per_class = []
|
155
|
-
for cls in range(num_classes):
|
156
|
-
tp = np.logical_and(y_pred == cls, y_true == cls).sum()
|
157
|
-
fp = np.logical_and(y_pred == cls, y_true != cls).sum()
|
158
|
-
fn = np.logical_and(y_pred != cls, y_true == cls).sum()
|
159
|
-
|
160
|
-
f1 = (2 * tp) / (2 * tp + fp + fn) if (2 * tp + fp + fn) > 0 else 0
|
161
|
-
f1_per_class.append(f1)
|
162
|
-
|
163
|
-
mean_f1 = np.mean(f1_per_class)
|
164
|
-
return f1_per_class, mean_f1
|
165
|
-
|
166
|
-
raise ValueError("Unsupported library for F1 Score.")
|