aplr 10.17.0__cp312-cp312-win_amd64.whl → 10.18.0__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aplr might be problematic. Click here for more details.
- aplr/aplr.py +229 -33
- aplr-10.18.0.dist-info/METADATA +34 -0
- aplr-10.18.0.dist-info/RECORD +8 -0
- aplr_cpp.cp312-win_amd64.pyd +0 -0
- aplr-10.17.0.dist-info/METADATA +0 -68
- aplr-10.17.0.dist-info/RECORD +0 -8
- {aplr-10.17.0.dist-info → aplr-10.18.0.dist-info}/WHEEL +0 -0
- {aplr-10.17.0.dist-info → aplr-10.18.0.dist-info}/licenses/LICENSE +0 -0
- {aplr-10.17.0.dist-info → aplr-10.18.0.dist-info}/top_level.txt +0 -0
aplr/aplr.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import List, Callable, Optional, Dict, Union
|
|
2
2
|
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
3
4
|
import aplr_cpp
|
|
4
5
|
import itertools
|
|
5
6
|
|
|
@@ -9,7 +10,134 @@ IntVector = np.ndarray
|
|
|
9
10
|
IntMatrix = np.ndarray
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
class
|
|
13
|
+
class BaseAPLR:
|
|
14
|
+
def _validate_X_fit_rows(self, X):
|
|
15
|
+
"""Checks if X has enough rows to be fitted."""
|
|
16
|
+
if (isinstance(X, np.ndarray) and X.shape[0] < 2) or (
|
|
17
|
+
isinstance(X, pd.DataFrame) and len(X) < 2
|
|
18
|
+
):
|
|
19
|
+
raise ValueError("Input X must have at least 2 rows to be fitted.")
|
|
20
|
+
|
|
21
|
+
def _common_X_preprocessing(self, X, is_fitting: bool, X_names=None):
|
|
22
|
+
"""Common preprocessing for fit and predict."""
|
|
23
|
+
is_dataframe_input = isinstance(X, pd.DataFrame)
|
|
24
|
+
|
|
25
|
+
if not is_dataframe_input:
|
|
26
|
+
try:
|
|
27
|
+
X_numeric = np.array(X, dtype=np.float64)
|
|
28
|
+
except (ValueError, TypeError) as e:
|
|
29
|
+
raise TypeError(
|
|
30
|
+
"Input X must be numeric if not a pandas DataFrame."
|
|
31
|
+
) from e
|
|
32
|
+
X = pd.DataFrame(X_numeric)
|
|
33
|
+
if is_fitting:
|
|
34
|
+
if X_names:
|
|
35
|
+
X.columns = X_names
|
|
36
|
+
else:
|
|
37
|
+
X.columns = [f"X{i}" for i in range(X.shape[1])]
|
|
38
|
+
elif hasattr(self, "X_names_") and len(self.X_names_) == X.shape[1]:
|
|
39
|
+
X.columns = self.X_names_
|
|
40
|
+
else: # X is already a DataFrame
|
|
41
|
+
X = X.copy() # Always copy to avoid modifying original
|
|
42
|
+
if not is_fitting and hasattr(self, "X_names_"):
|
|
43
|
+
# Check if input columns for prediction match training columns (before OHE)
|
|
44
|
+
if set(X.columns) != set(self.X_names_):
|
|
45
|
+
raise ValueError(
|
|
46
|
+
"Input columns for prediction do not match training columns."
|
|
47
|
+
)
|
|
48
|
+
X = X[self.X_names_] # Ensure order of original columns
|
|
49
|
+
|
|
50
|
+
if is_fitting:
|
|
51
|
+
self.X_names_ = list(X.columns)
|
|
52
|
+
self.categorical_features_ = list(
|
|
53
|
+
X.select_dtypes(include=["category", "object"]).columns
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if self.categorical_features_:
|
|
57
|
+
X = pd.get_dummies(X, columns=self.categorical_features_, dummy_na=False)
|
|
58
|
+
if is_fitting:
|
|
59
|
+
self.ohe_columns_ = list(X.columns)
|
|
60
|
+
else:
|
|
61
|
+
missing_cols = set(self.ohe_columns_) - set(X.columns)
|
|
62
|
+
for c in missing_cols:
|
|
63
|
+
X[c] = 0
|
|
64
|
+
X = X[self.ohe_columns_] # Enforce column order
|
|
65
|
+
|
|
66
|
+
if is_fitting:
|
|
67
|
+
self.na_imputed_cols_ = [col for col in X.columns if X[col].isnull().any()]
|
|
68
|
+
|
|
69
|
+
if self.na_imputed_cols_:
|
|
70
|
+
for col in self.na_imputed_cols_:
|
|
71
|
+
X[col + "_missing"] = X[col].isnull().astype(int)
|
|
72
|
+
|
|
73
|
+
if not is_fitting:
|
|
74
|
+
for col in self.median_values_:
|
|
75
|
+
if col in X.columns:
|
|
76
|
+
X[col] = X[col].fillna(self.median_values_[col])
|
|
77
|
+
|
|
78
|
+
return X
|
|
79
|
+
|
|
80
|
+
def _preprocess_X_fit(self, X, X_names, sample_weight):
|
|
81
|
+
if sample_weight.size > 0:
|
|
82
|
+
if sample_weight.ndim != 1:
|
|
83
|
+
raise ValueError("sample_weight must be a 1D array.")
|
|
84
|
+
if len(sample_weight) != X.shape[0]:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
"sample_weight must have the same number of rows as X."
|
|
87
|
+
)
|
|
88
|
+
if np.any(np.isnan(sample_weight)) or np.any(np.isinf(sample_weight)):
|
|
89
|
+
raise ValueError("sample_weight cannot contain nan or infinite values.")
|
|
90
|
+
if np.any(sample_weight < 0):
|
|
91
|
+
raise ValueError("sample_weight cannot contain negative values.")
|
|
92
|
+
X = self._common_X_preprocessing(X, is_fitting=True, X_names=X_names)
|
|
93
|
+
self.median_values_ = {}
|
|
94
|
+
numeric_cols_for_median = [col for col in X.columns if "_missing" not in col]
|
|
95
|
+
for col in numeric_cols_for_median:
|
|
96
|
+
missing_mask = X[col].isnull()
|
|
97
|
+
|
|
98
|
+
if sample_weight.size > 0:
|
|
99
|
+
valid_indices = ~missing_mask
|
|
100
|
+
col_data = X.loc[valid_indices, col]
|
|
101
|
+
col_weights = sample_weight[valid_indices]
|
|
102
|
+
if col_data.empty:
|
|
103
|
+
median_val = 0
|
|
104
|
+
else:
|
|
105
|
+
col_data_np = col_data.to_numpy()
|
|
106
|
+
sort_indices = np.argsort(col_data_np, kind="stable")
|
|
107
|
+
sorted_data = col_data_np[sort_indices]
|
|
108
|
+
sorted_weights = col_weights[sort_indices]
|
|
109
|
+
|
|
110
|
+
cumulative_weights = np.cumsum(sorted_weights)
|
|
111
|
+
total_weight = cumulative_weights[-1]
|
|
112
|
+
|
|
113
|
+
median_weight_index = np.searchsorted(
|
|
114
|
+
cumulative_weights, total_weight / 2.0
|
|
115
|
+
)
|
|
116
|
+
if median_weight_index >= len(sorted_data):
|
|
117
|
+
median_weight_index = len(sorted_data) - 1
|
|
118
|
+
median_val = sorted_data[median_weight_index]
|
|
119
|
+
else:
|
|
120
|
+
median_val = X[col].median()
|
|
121
|
+
|
|
122
|
+
if pd.isna(median_val):
|
|
123
|
+
median_val = 0
|
|
124
|
+
|
|
125
|
+
self.median_values_[col] = median_val
|
|
126
|
+
X[col] = X[col].fillna(median_val)
|
|
127
|
+
|
|
128
|
+
self.final_training_columns_ = list(X.columns)
|
|
129
|
+
return X.values.astype(np.float64), list(X.columns)
|
|
130
|
+
|
|
131
|
+
def _preprocess_X_predict(self, X):
|
|
132
|
+
X = self._common_X_preprocessing(X, is_fitting=False)
|
|
133
|
+
|
|
134
|
+
if hasattr(self, "final_training_columns_"):
|
|
135
|
+
X = X[self.final_training_columns_]
|
|
136
|
+
|
|
137
|
+
return X.values.astype(np.float64)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class APLRRegressor(BaseAPLR):
|
|
13
141
|
def __init__(
|
|
14
142
|
self,
|
|
15
143
|
m: int = 3000,
|
|
@@ -127,6 +255,13 @@ class APLRRegressor:
|
|
|
127
255
|
self.mean_bias_correction = mean_bias_correction
|
|
128
256
|
self.faster_convergence = faster_convergence
|
|
129
257
|
|
|
258
|
+
# Data transformations
|
|
259
|
+
self.median_values_ = {}
|
|
260
|
+
self.categorical_features_ = []
|
|
261
|
+
self.ohe_columns_ = []
|
|
262
|
+
self.na_imputed_cols_ = []
|
|
263
|
+
self.X_names_ = []
|
|
264
|
+
|
|
130
265
|
# Creating aplr_cpp and setting parameters
|
|
131
266
|
self.APLRRegressor = aplr_cpp.APLRRegressor()
|
|
132
267
|
self.__set_params_cpp()
|
|
@@ -192,7 +327,7 @@ class APLRRegressor:
|
|
|
192
327
|
|
|
193
328
|
def fit(
|
|
194
329
|
self,
|
|
195
|
-
X: FloatMatrix,
|
|
330
|
+
X: Union[pd.DataFrame, FloatMatrix],
|
|
196
331
|
y: FloatVector,
|
|
197
332
|
sample_weight: FloatVector = np.empty(0),
|
|
198
333
|
X_names: List[str] = [],
|
|
@@ -207,12 +342,16 @@ class APLRRegressor:
|
|
|
207
342
|
predictor_penalties_for_interactions: List[float] = [],
|
|
208
343
|
predictor_min_observations_in_split: List[int] = [],
|
|
209
344
|
):
|
|
345
|
+
self._validate_X_fit_rows(X)
|
|
210
346
|
self.__set_params_cpp()
|
|
347
|
+
X_transformed, X_names_transformed = self._preprocess_X_fit(
|
|
348
|
+
X, X_names, sample_weight
|
|
349
|
+
)
|
|
211
350
|
self.APLRRegressor.fit(
|
|
212
|
-
|
|
351
|
+
X_transformed,
|
|
213
352
|
y,
|
|
214
353
|
sample_weight,
|
|
215
|
-
|
|
354
|
+
X_names_transformed,
|
|
216
355
|
cv_observations,
|
|
217
356
|
prioritized_predictors_indexes,
|
|
218
357
|
monotonic_constraints,
|
|
@@ -226,42 +365,65 @@ class APLRRegressor:
|
|
|
226
365
|
)
|
|
227
366
|
|
|
228
367
|
def predict(
|
|
229
|
-
self,
|
|
368
|
+
self,
|
|
369
|
+
X: Union[pd.DataFrame, FloatMatrix],
|
|
370
|
+
cap_predictions_to_minmax_in_training: bool = True,
|
|
230
371
|
) -> FloatVector:
|
|
231
372
|
if self.link_function == "custom_function":
|
|
232
373
|
self.APLRRegressor.calculate_custom_transform_linear_predictor_to_predictions_function = (
|
|
233
374
|
self.calculate_custom_transform_linear_predictor_to_predictions_function
|
|
234
375
|
)
|
|
235
|
-
|
|
376
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
377
|
+
return self.APLRRegressor.predict(
|
|
378
|
+
X_transformed, cap_predictions_to_minmax_in_training
|
|
379
|
+
)
|
|
236
380
|
|
|
237
381
|
def set_term_names(self, X_names: List[str]):
|
|
238
382
|
self.APLRRegressor.set_term_names(X_names)
|
|
239
383
|
|
|
240
384
|
def calculate_feature_importance(
|
|
241
|
-
self,
|
|
385
|
+
self,
|
|
386
|
+
X: Union[pd.DataFrame, FloatMatrix],
|
|
387
|
+
sample_weight: FloatVector = np.empty(0),
|
|
242
388
|
) -> FloatVector:
|
|
243
|
-
|
|
389
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
390
|
+
return self.APLRRegressor.calculate_feature_importance(
|
|
391
|
+
X_transformed, sample_weight
|
|
392
|
+
)
|
|
244
393
|
|
|
245
394
|
def calculate_term_importance(
|
|
246
|
-
self,
|
|
395
|
+
self,
|
|
396
|
+
X: Union[pd.DataFrame, FloatMatrix],
|
|
397
|
+
sample_weight: FloatVector = np.empty(0),
|
|
247
398
|
) -> FloatVector:
|
|
248
|
-
|
|
399
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
400
|
+
return self.APLRRegressor.calculate_term_importance(
|
|
401
|
+
X_transformed, sample_weight
|
|
402
|
+
)
|
|
249
403
|
|
|
250
|
-
def calculate_local_feature_contribution(
|
|
251
|
-
|
|
404
|
+
def calculate_local_feature_contribution(
|
|
405
|
+
self, X: Union[pd.DataFrame, FloatMatrix]
|
|
406
|
+
) -> FloatMatrix:
|
|
407
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
408
|
+
return self.APLRRegressor.calculate_local_feature_contribution(X_transformed)
|
|
252
409
|
|
|
253
|
-
def calculate_local_term_contribution(
|
|
254
|
-
|
|
410
|
+
def calculate_local_term_contribution(
|
|
411
|
+
self, X: Union[pd.DataFrame, FloatMatrix]
|
|
412
|
+
) -> FloatMatrix:
|
|
413
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
414
|
+
return self.APLRRegressor.calculate_local_term_contribution(X_transformed)
|
|
255
415
|
|
|
256
416
|
def calculate_local_contribution_from_selected_terms(
|
|
257
|
-
self, X: FloatMatrix, predictor_indexes: List[int]
|
|
417
|
+
self, X: Union[pd.DataFrame, FloatMatrix], predictor_indexes: List[int]
|
|
258
418
|
) -> FloatVector:
|
|
419
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
259
420
|
return self.APLRRegressor.calculate_local_contribution_from_selected_terms(
|
|
260
|
-
|
|
421
|
+
X_transformed, predictor_indexes
|
|
261
422
|
)
|
|
262
423
|
|
|
263
|
-
def calculate_terms(self, X: FloatMatrix) -> FloatMatrix:
|
|
264
|
-
|
|
424
|
+
def calculate_terms(self, X: Union[pd.DataFrame, FloatMatrix]) -> FloatMatrix:
|
|
425
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
426
|
+
return self.APLRRegressor.calculate_terms(X_transformed)
|
|
265
427
|
|
|
266
428
|
def get_term_names(self) -> List[str]:
|
|
267
429
|
return self.APLRRegressor.get_term_names()
|
|
@@ -483,7 +645,7 @@ class APLRRegressor:
|
|
|
483
645
|
return self
|
|
484
646
|
|
|
485
647
|
|
|
486
|
-
class APLRClassifier:
|
|
648
|
+
class APLRClassifier(BaseAPLR):
|
|
487
649
|
def __init__(
|
|
488
650
|
self,
|
|
489
651
|
m: int = 3000,
|
|
@@ -534,6 +696,13 @@ class APLRClassifier:
|
|
|
534
696
|
self.max_terms = max_terms
|
|
535
697
|
self.ridge_penalty = ridge_penalty
|
|
536
698
|
|
|
699
|
+
# Data transformations
|
|
700
|
+
self.median_values_ = {}
|
|
701
|
+
self.categorical_features_ = []
|
|
702
|
+
self.ohe_columns_ = []
|
|
703
|
+
self.na_imputed_cols_ = []
|
|
704
|
+
self.X_names_ = []
|
|
705
|
+
|
|
537
706
|
# Creating aplr_cpp and setting parameters
|
|
538
707
|
self.APLRClassifier = aplr_cpp.APLRClassifier()
|
|
539
708
|
self.__set_params_cpp()
|
|
@@ -571,8 +740,8 @@ class APLRClassifier:
|
|
|
571
740
|
|
|
572
741
|
def fit(
|
|
573
742
|
self,
|
|
574
|
-
X: FloatMatrix,
|
|
575
|
-
y: List[str],
|
|
743
|
+
X: Union[pd.DataFrame, FloatMatrix],
|
|
744
|
+
y: Union[FloatVector, List[str]],
|
|
576
745
|
sample_weight: FloatVector = np.empty(0),
|
|
577
746
|
X_names: List[str] = [],
|
|
578
747
|
cv_observations: IntMatrix = np.empty([0, 0]),
|
|
@@ -584,12 +753,22 @@ class APLRClassifier:
|
|
|
584
753
|
predictor_penalties_for_interactions: List[float] = [],
|
|
585
754
|
predictor_min_observations_in_split: List[int] = [],
|
|
586
755
|
):
|
|
756
|
+
self._validate_X_fit_rows(X)
|
|
587
757
|
self.__set_params_cpp()
|
|
758
|
+
X_transformed, X_names_transformed = self._preprocess_X_fit(
|
|
759
|
+
X, X_names, sample_weight
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
if isinstance(y, np.ndarray):
|
|
763
|
+
y = y.astype(str).tolist()
|
|
764
|
+
elif isinstance(y, list) and y and not isinstance(y[0], str):
|
|
765
|
+
y = [str(val) for val in y]
|
|
766
|
+
|
|
588
767
|
self.APLRClassifier.fit(
|
|
589
|
-
|
|
768
|
+
X_transformed,
|
|
590
769
|
y,
|
|
591
770
|
sample_weight,
|
|
592
|
-
|
|
771
|
+
X_names_transformed,
|
|
593
772
|
cv_observations,
|
|
594
773
|
prioritized_predictors_indexes,
|
|
595
774
|
monotonic_constraints,
|
|
@@ -603,19 +782,30 @@ class APLRClassifier:
|
|
|
603
782
|
self.classes_ = np.arange(len(self.APLRClassifier.get_categories()))
|
|
604
783
|
|
|
605
784
|
def predict_class_probabilities(
|
|
606
|
-
self,
|
|
785
|
+
self,
|
|
786
|
+
X: Union[pd.DataFrame, FloatMatrix],
|
|
787
|
+
cap_predictions_to_minmax_in_training: bool = False,
|
|
607
788
|
) -> FloatMatrix:
|
|
789
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
608
790
|
return self.APLRClassifier.predict_class_probabilities(
|
|
609
|
-
|
|
791
|
+
X_transformed, cap_predictions_to_minmax_in_training
|
|
610
792
|
)
|
|
611
793
|
|
|
612
794
|
def predict(
|
|
613
|
-
self,
|
|
795
|
+
self,
|
|
796
|
+
X: Union[pd.DataFrame, FloatMatrix],
|
|
797
|
+
cap_predictions_to_minmax_in_training: bool = False,
|
|
614
798
|
) -> List[str]:
|
|
615
|
-
|
|
799
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
800
|
+
return self.APLRClassifier.predict(
|
|
801
|
+
X_transformed, cap_predictions_to_minmax_in_training
|
|
802
|
+
)
|
|
616
803
|
|
|
617
|
-
def calculate_local_feature_contribution(
|
|
618
|
-
|
|
804
|
+
def calculate_local_feature_contribution(
|
|
805
|
+
self, X: Union[pd.DataFrame, FloatMatrix]
|
|
806
|
+
) -> FloatMatrix:
|
|
807
|
+
X_transformed = self._preprocess_X_predict(X)
|
|
808
|
+
return self.APLRClassifier.calculate_local_feature_contribution(X_transformed)
|
|
619
809
|
|
|
620
810
|
def get_categories(self) -> List[str]:
|
|
621
811
|
return self.APLRClassifier.get_categories()
|
|
@@ -724,7 +914,7 @@ class APLRTuner:
|
|
|
724
914
|
grid = [dict(zip(keys, combination)) for combination in combinations]
|
|
725
915
|
return grid
|
|
726
916
|
|
|
727
|
-
def fit(self, X: FloatMatrix, y: FloatVector, **kwargs):
|
|
917
|
+
def fit(self, X: Union[pd.DataFrame, FloatMatrix], y: FloatVector, **kwargs):
|
|
728
918
|
self.cv_results: List[Dict[str, float]] = []
|
|
729
919
|
best_validation_result = np.inf
|
|
730
920
|
for params in self.parameter_grid:
|
|
@@ -742,10 +932,14 @@ class APLRTuner:
|
|
|
742
932
|
self.best_model = model
|
|
743
933
|
self.cv_results = sorted(self.cv_results, key=lambda x: x["cv_error"])
|
|
744
934
|
|
|
745
|
-
def predict(
|
|
935
|
+
def predict(
|
|
936
|
+
self, X: Union[pd.DataFrame, FloatMatrix], **kwargs
|
|
937
|
+
) -> Union[FloatVector, List[str]]:
|
|
746
938
|
return self.best_model.predict(X, **kwargs)
|
|
747
939
|
|
|
748
|
-
def predict_class_probabilities(
|
|
940
|
+
def predict_class_probabilities(
|
|
941
|
+
self, X: Union[pd.DataFrame, FloatMatrix], **kwargs
|
|
942
|
+
) -> FloatMatrix:
|
|
749
943
|
if self.is_regressor == False:
|
|
750
944
|
return self.best_model.predict_class_probabilities(X, **kwargs)
|
|
751
945
|
else:
|
|
@@ -753,7 +947,9 @@ class APLRTuner:
|
|
|
753
947
|
"predict_class_probabilities is only possible when is_regressor is False"
|
|
754
948
|
)
|
|
755
949
|
|
|
756
|
-
def predict_proba(
|
|
950
|
+
def predict_proba(
|
|
951
|
+
self, X: Union[pd.DataFrame, FloatMatrix], **kwargs
|
|
952
|
+
) -> FloatMatrix:
|
|
757
953
|
return self.predict_class_probabilities(X, **kwargs)
|
|
758
954
|
|
|
759
955
|
def get_best_estimator(self) -> Union[APLRClassifier, APLRRegressor]:
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aplr
|
|
3
|
+
Version: 10.18.0
|
|
4
|
+
Summary: Automatic Piecewise Linear Regression
|
|
5
|
+
Home-page: https://github.com/ottenbreit-data-science/aplr
|
|
6
|
+
Author: Mathias von Ottenbreit
|
|
7
|
+
Author-email: ottenbreitdatascience@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Platform: Windows
|
|
10
|
+
Platform: Linux
|
|
11
|
+
Platform: MacOS
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: numpy>=1.11
|
|
17
|
+
Requires-Dist: pandas>=1.0.0
|
|
18
|
+
Provides-Extra: plots
|
|
19
|
+
Requires-Dist: matplotlib>=3.0; extra == "plots"
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: license
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
Dynamic: platform
|
|
29
|
+
Dynamic: provides-extra
|
|
30
|
+
Dynamic: requires-dist
|
|
31
|
+
Dynamic: requires-python
|
|
32
|
+
Dynamic: summary
|
|
33
|
+
|
|
34
|
+
The documentation for Automatic Piecewise Linear Regression is available at [https://github.com/ottenbreit-data-science/aplr](https://github.com/ottenbreit-data-science/aplr).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
aplr_cpp.cp312-win_amd64.pyd,sha256=6zNh0yut-s9QgpyPomUssWYxQwbop4vuIHOHRAdv1pk,664576
|
|
2
|
+
aplr/__init__.py,sha256=oDFSgVytP_qQ8ilun6oHxKr-DYEeqjEQp5FciX45lls,21
|
|
3
|
+
aplr/aplr.py,sha256=cEI63m6-5U1VVou-CfKIQ85ys0DL8rqi9ghWFBK3BxY,41090
|
|
4
|
+
aplr-10.18.0.dist-info/licenses/LICENSE,sha256=YOMo-RaL4P7edMZGD96-NskKpxyMZdP3-WiiMMmihNk,1134
|
|
5
|
+
aplr-10.18.0.dist-info/METADATA,sha256=Wsrqg1LzAYOKTuAcjW8s3YXmJUsjvHx1k2AWTlNntiY,1048
|
|
6
|
+
aplr-10.18.0.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
|
|
7
|
+
aplr-10.18.0.dist-info/top_level.txt,sha256=DXVC0RIFGpzVnPeKWAZTXQdJheOEZL51Wip6Fx7zbR4,14
|
|
8
|
+
aplr-10.18.0.dist-info/RECORD,,
|
aplr_cpp.cp312-win_amd64.pyd
CHANGED
|
Binary file
|
aplr-10.17.0.dist-info/METADATA
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: aplr
|
|
3
|
-
Version: 10.17.0
|
|
4
|
-
Summary: Automatic Piecewise Linear Regression
|
|
5
|
-
Home-page: https://github.com/ottenbreit-data-science/aplr
|
|
6
|
-
Author: Mathias von Ottenbreit
|
|
7
|
-
Author-email: ottenbreitdatascience@gmail.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Platform: Windows
|
|
10
|
-
Platform: Linux
|
|
11
|
-
Platform: MacOS
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Requires-Python: >=3.8
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
License-File: LICENSE
|
|
16
|
-
Requires-Dist: numpy>=1.11
|
|
17
|
-
Provides-Extra: plots
|
|
18
|
-
Requires-Dist: matplotlib>=3.0; extra == "plots"
|
|
19
|
-
Dynamic: author
|
|
20
|
-
Dynamic: author-email
|
|
21
|
-
Dynamic: classifier
|
|
22
|
-
Dynamic: description
|
|
23
|
-
Dynamic: description-content-type
|
|
24
|
-
Dynamic: home-page
|
|
25
|
-
Dynamic: license
|
|
26
|
-
Dynamic: license-file
|
|
27
|
-
Dynamic: platform
|
|
28
|
-
Dynamic: provides-extra
|
|
29
|
-
Dynamic: requires-dist
|
|
30
|
-
Dynamic: requires-python
|
|
31
|
-
Dynamic: summary
|
|
32
|
-
|
|
33
|
-
# APLR
|
|
34
|
-
**Automatic Piecewise Linear Regression**
|
|
35
|
-
|
|
36
|
-
## About
|
|
37
|
-
APLR allows you to build predictive and interpretable regression or classification machine learning models in Python, using the Automatic Piecewise Linear Regression (APLR) methodology developed by Mathias von Ottenbreit. APLR often rivals tree-based methods in predictive accuracy, while offering smoother, more interpretable predictions.
|
|
38
|
-
|
|
39
|
-
For further details, see the [documentation](https://github.com/ottenbreit-data-science/aplr/tree/main/documentation). You may also read the published article for additional insights: [Link 1](https://link.springer.com/article/10.1007/s00180-024-01475-4) and [Link 2](https://rdcu.be/dz7bF). Additional functionality has been added since the article was published.
|
|
40
|
-
|
|
41
|
-
## Installation
|
|
42
|
-
To install APLR, use the following command:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
pip install aplr
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
To include dependencies for plotting, use this command instead:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
pip install aplr[plots]
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Availability
|
|
55
|
-
APLR is available for Windows, most Linux distributions, and macOS.
|
|
56
|
-
|
|
57
|
-
## Usage
|
|
58
|
-
Example Python scripts are available [here](https://github.com/ottenbreit-data-science/aplr/tree/main/examples).
|
|
59
|
-
|
|
60
|
-
## Sponsorship
|
|
61
|
-
Consider sponsoring Von Ottenbreit Data Science by clicking the **Sponsor** button on the repository. Sufficient funding will help maintain and further develop APLR.
|
|
62
|
-
|
|
63
|
-
## API Reference
|
|
64
|
-
- [API reference for regression](https://github.com/ottenbreit-data-science/aplr/blob/main/API_REFERENCE_FOR_REGRESSION.md)
|
|
65
|
-
- [API reference for classification](https://github.com/ottenbreit-data-science/aplr/blob/main/API_REFERENCE_FOR_CLASSIFICATION.md)
|
|
66
|
-
|
|
67
|
-
## Contact Information
|
|
68
|
-
For inquiries, please email: [ottenbreitdatascience@gmail.com](mailto:ottenbreitdatascience@gmail.com)
|
aplr-10.17.0.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
aplr_cpp.cp312-win_amd64.pyd,sha256=p3JsaFlbPWuLMf9fsxX0v0rKymeIxK8_N1ze58HGxTI,664064
|
|
2
|
-
aplr/__init__.py,sha256=oDFSgVytP_qQ8ilun6oHxKr-DYEeqjEQp5FciX45lls,21
|
|
3
|
-
aplr/aplr.py,sha256=obmpNNppOrnzQxhYzShYzNe02lQv17-YRWUklf8IXds,33302
|
|
4
|
-
aplr-10.17.0.dist-info/licenses/LICENSE,sha256=YOMo-RaL4P7edMZGD96-NskKpxyMZdP3-WiiMMmihNk,1134
|
|
5
|
-
aplr-10.17.0.dist-info/METADATA,sha256=0-e_AeSLFoIr-ofT8xyweTMgwLbOvkdmNRGwOtgAo3g,2627
|
|
6
|
-
aplr-10.17.0.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
|
|
7
|
-
aplr-10.17.0.dist-info/top_level.txt,sha256=DXVC0RIFGpzVnPeKWAZTXQdJheOEZL51Wip6Fx7zbR4,14
|
|
8
|
-
aplr-10.17.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|