aplr 10.9.0__cp311-cp311-macosx_11_0_x86_64.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/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .aplr import *
aplr/aplr.py ADDED
@@ -0,0 +1,608 @@
1
+ from typing import List, Callable, Optional, Dict, Union
2
+ import numpy as np
3
+ import aplr_cpp
4
+ import itertools
5
+
6
+ FloatVector = np.ndarray
7
+ FloatMatrix = np.ndarray
8
+ IntVector = np.ndarray
9
+ IntMatrix = np.ndarray
10
+
11
+
12
+ class APLRRegressor:
13
+ def __init__(
14
+ self,
15
+ m: int = 3000,
16
+ v: float = 0.5,
17
+ random_state: int = 0,
18
+ loss_function: str = "mse",
19
+ link_function: str = "identity",
20
+ n_jobs: int = 0,
21
+ cv_folds: int = 5,
22
+ bins: int = 300,
23
+ max_interaction_level: int = 1,
24
+ max_interactions: int = 100000,
25
+ min_observations_in_split: int = 4,
26
+ ineligible_boosting_steps_added: int = 15,
27
+ max_eligible_terms: int = 7,
28
+ verbosity: int = 0,
29
+ dispersion_parameter: float = 1.5,
30
+ validation_tuning_metric: str = "default",
31
+ quantile: float = 0.5,
32
+ calculate_custom_validation_error_function: Optional[
33
+ Callable[
34
+ [
35
+ FloatVector,
36
+ FloatVector,
37
+ FloatVector,
38
+ FloatVector,
39
+ FloatMatrix,
40
+ ],
41
+ float,
42
+ ]
43
+ ] = None,
44
+ calculate_custom_loss_function: Optional[
45
+ Callable[
46
+ [
47
+ FloatVector,
48
+ FloatVector,
49
+ FloatVector,
50
+ FloatVector,
51
+ FloatMatrix,
52
+ ],
53
+ float,
54
+ ]
55
+ ] = None,
56
+ calculate_custom_negative_gradient_function: Optional[
57
+ Callable[
58
+ [FloatVector, FloatVector, FloatVector, FloatMatrix],
59
+ FloatVector,
60
+ ]
61
+ ] = None,
62
+ calculate_custom_transform_linear_predictor_to_predictions_function: Optional[
63
+ Callable[[FloatVector], FloatVector]
64
+ ] = None,
65
+ calculate_custom_differentiate_predictions_wrt_linear_predictor_function: Optional[
66
+ Callable[[FloatVector], FloatVector]
67
+ ] = None,
68
+ boosting_steps_before_interactions_are_allowed: int = 0,
69
+ monotonic_constraints_ignore_interactions: bool = False,
70
+ group_mse_by_prediction_bins: int = 10,
71
+ group_mse_cycle_min_obs_in_bin: int = 30,
72
+ early_stopping_rounds: int = 200,
73
+ num_first_steps_with_linear_effects_only: int = 0,
74
+ penalty_for_non_linearity: float = 0.0,
75
+ penalty_for_interactions: float = 0.0,
76
+ max_terms: int = 0,
77
+ ridge_penalty: float = 0.0001,
78
+ ):
79
+ self.m = m
80
+ self.v = v
81
+ self.random_state = random_state
82
+ self.loss_function = loss_function
83
+ self.link_function = link_function
84
+ self.n_jobs = n_jobs
85
+ self.cv_folds = cv_folds
86
+ self.bins = bins
87
+ self.max_interaction_level = max_interaction_level
88
+ self.max_interactions = max_interactions
89
+ self.min_observations_in_split = min_observations_in_split
90
+ self.ineligible_boosting_steps_added = ineligible_boosting_steps_added
91
+ self.max_eligible_terms = max_eligible_terms
92
+ self.verbosity = verbosity
93
+ self.dispersion_parameter = dispersion_parameter
94
+ self.validation_tuning_metric = validation_tuning_metric
95
+ self.quantile = quantile
96
+ self.calculate_custom_validation_error_function = (
97
+ calculate_custom_validation_error_function
98
+ )
99
+ self.calculate_custom_loss_function = calculate_custom_loss_function
100
+ self.calculate_custom_negative_gradient_function = (
101
+ calculate_custom_negative_gradient_function
102
+ )
103
+ self.calculate_custom_transform_linear_predictor_to_predictions_function = (
104
+ calculate_custom_transform_linear_predictor_to_predictions_function
105
+ )
106
+ self.calculate_custom_differentiate_predictions_wrt_linear_predictor_function = (
107
+ calculate_custom_differentiate_predictions_wrt_linear_predictor_function
108
+ )
109
+ self.boosting_steps_before_interactions_are_allowed = (
110
+ boosting_steps_before_interactions_are_allowed
111
+ )
112
+ self.monotonic_constraints_ignore_interactions = (
113
+ monotonic_constraints_ignore_interactions
114
+ )
115
+ self.group_mse_by_prediction_bins = group_mse_by_prediction_bins
116
+ self.group_mse_cycle_min_obs_in_bin = group_mse_cycle_min_obs_in_bin
117
+ self.early_stopping_rounds = early_stopping_rounds
118
+ self.num_first_steps_with_linear_effects_only = (
119
+ num_first_steps_with_linear_effects_only
120
+ )
121
+ self.penalty_for_non_linearity = penalty_for_non_linearity
122
+ self.penalty_for_interactions = penalty_for_interactions
123
+ self.max_terms = max_terms
124
+ self.ridge_penalty = ridge_penalty
125
+
126
+ # Creating aplr_cpp and setting parameters
127
+ self.APLRRegressor = aplr_cpp.APLRRegressor()
128
+ self.__set_params_cpp()
129
+
130
+ # Sets parameters for aplr_cpp.APLRRegressor cpp object
131
+ def __set_params_cpp(self):
132
+ self.APLRRegressor.m = self.m
133
+ self.APLRRegressor.v = self.v
134
+ self.APLRRegressor.random_state = self.random_state
135
+ self.APLRRegressor.loss_function = self.loss_function
136
+ self.APLRRegressor.link_function = self.link_function
137
+ self.APLRRegressor.n_jobs = self.n_jobs
138
+ self.APLRRegressor.cv_folds = self.cv_folds
139
+ self.APLRRegressor.bins = self.bins
140
+ self.APLRRegressor.max_interaction_level = self.max_interaction_level
141
+ self.APLRRegressor.max_interactions = self.max_interactions
142
+ self.APLRRegressor.min_observations_in_split = self.min_observations_in_split
143
+ self.APLRRegressor.ineligible_boosting_steps_added = (
144
+ self.ineligible_boosting_steps_added
145
+ )
146
+ self.APLRRegressor.max_eligible_terms = self.max_eligible_terms
147
+ self.APLRRegressor.verbosity = self.verbosity
148
+ self.APLRRegressor.dispersion_parameter = self.dispersion_parameter
149
+ self.APLRRegressor.validation_tuning_metric = self.validation_tuning_metric
150
+ self.APLRRegressor.quantile = self.quantile
151
+ self.APLRRegressor.calculate_custom_validation_error_function = (
152
+ self.calculate_custom_validation_error_function
153
+ )
154
+ self.APLRRegressor.calculate_custom_loss_function = (
155
+ self.calculate_custom_loss_function
156
+ )
157
+ self.APLRRegressor.calculate_custom_negative_gradient_function = (
158
+ self.calculate_custom_negative_gradient_function
159
+ )
160
+ self.APLRRegressor.calculate_custom_transform_linear_predictor_to_predictions_function = (
161
+ self.calculate_custom_transform_linear_predictor_to_predictions_function
162
+ )
163
+ self.APLRRegressor.calculate_custom_differentiate_predictions_wrt_linear_predictor_function = (
164
+ self.calculate_custom_differentiate_predictions_wrt_linear_predictor_function
165
+ )
166
+ self.APLRRegressor.boosting_steps_before_interactions_are_allowed = (
167
+ self.boosting_steps_before_interactions_are_allowed
168
+ )
169
+ self.APLRRegressor.monotonic_constraints_ignore_interactions = (
170
+ self.monotonic_constraints_ignore_interactions
171
+ )
172
+ self.APLRRegressor.group_mse_by_prediction_bins = (
173
+ self.group_mse_by_prediction_bins
174
+ )
175
+ self.APLRRegressor.group_mse_cycle_min_obs_in_bin = (
176
+ self.group_mse_cycle_min_obs_in_bin
177
+ )
178
+ self.APLRRegressor.early_stopping_rounds = self.early_stopping_rounds
179
+ self.APLRRegressor.num_first_steps_with_linear_effects_only = (
180
+ self.num_first_steps_with_linear_effects_only
181
+ )
182
+ self.APLRRegressor.penalty_for_non_linearity = self.penalty_for_non_linearity
183
+ self.APLRRegressor.penalty_for_interactions = self.penalty_for_interactions
184
+ self.APLRRegressor.max_terms = self.max_terms
185
+ self.APLRRegressor.ridge_penalty = self.ridge_penalty
186
+
187
+ def fit(
188
+ self,
189
+ X: FloatMatrix,
190
+ y: FloatVector,
191
+ sample_weight: FloatVector = np.empty(0),
192
+ X_names: List[str] = [],
193
+ cv_observations: IntMatrix = np.empty([0, 0]),
194
+ prioritized_predictors_indexes: List[int] = [],
195
+ monotonic_constraints: List[int] = [],
196
+ group: FloatVector = np.empty(0),
197
+ interaction_constraints: List[List[int]] = [],
198
+ other_data: FloatMatrix = np.empty([0, 0]),
199
+ predictor_learning_rates: List[float] = [],
200
+ predictor_penalties_for_non_linearity: List[float] = [],
201
+ predictor_penalties_for_interactions: List[float] = [],
202
+ predictor_min_observations_in_split: List[int] = [],
203
+ ):
204
+ self.__set_params_cpp()
205
+ self.APLRRegressor.fit(
206
+ X,
207
+ y,
208
+ sample_weight,
209
+ X_names,
210
+ cv_observations,
211
+ prioritized_predictors_indexes,
212
+ monotonic_constraints,
213
+ group,
214
+ interaction_constraints,
215
+ other_data,
216
+ predictor_learning_rates,
217
+ predictor_penalties_for_non_linearity,
218
+ predictor_penalties_for_interactions,
219
+ predictor_min_observations_in_split,
220
+ )
221
+
222
+ def predict(
223
+ self, X: FloatMatrix, cap_predictions_to_minmax_in_training: bool = True
224
+ ) -> FloatVector:
225
+ if self.link_function == "custom_function":
226
+ self.APLRRegressor.calculate_custom_transform_linear_predictor_to_predictions_function = (
227
+ self.calculate_custom_transform_linear_predictor_to_predictions_function
228
+ )
229
+ return self.APLRRegressor.predict(X, cap_predictions_to_minmax_in_training)
230
+
231
+ def set_term_names(self, X_names: List[str]):
232
+ self.APLRRegressor.set_term_names(X_names)
233
+
234
+ def calculate_feature_importance(
235
+ self, X: FloatMatrix, sample_weight: FloatVector = np.empty(0)
236
+ ) -> FloatVector:
237
+ return self.APLRRegressor.calculate_feature_importance(X, sample_weight)
238
+
239
+ def calculate_term_importance(
240
+ self, X: FloatMatrix, sample_weight: FloatVector = np.empty(0)
241
+ ) -> FloatVector:
242
+ return self.APLRRegressor.calculate_term_importance(X, sample_weight)
243
+
244
+ def calculate_local_feature_contribution(self, X: FloatMatrix) -> FloatMatrix:
245
+ return self.APLRRegressor.calculate_local_feature_contribution(X)
246
+
247
+ def calculate_local_term_contribution(self, X: FloatMatrix) -> FloatMatrix:
248
+ return self.APLRRegressor.calculate_local_term_contribution(X)
249
+
250
+ def calculate_local_contribution_from_selected_terms(
251
+ self, X: FloatMatrix, predictor_indexes: List[int]
252
+ ) -> FloatVector:
253
+ return self.APLRRegressor.calculate_local_contribution_from_selected_terms(
254
+ X, predictor_indexes
255
+ )
256
+
257
+ def calculate_terms(self, X: FloatMatrix) -> FloatMatrix:
258
+ return self.APLRRegressor.calculate_terms(X)
259
+
260
+ def get_term_names(self) -> List[str]:
261
+ return self.APLRRegressor.get_term_names()
262
+
263
+ def get_term_affiliations(self) -> List[str]:
264
+ return self.APLRRegressor.get_term_affiliations()
265
+
266
+ def get_unique_term_affiliations(self) -> List[str]:
267
+ return self.APLRRegressor.get_unique_term_affiliations()
268
+
269
+ def get_base_predictors_in_each_unique_term_affiliation(self) -> List[List[int]]:
270
+ return self.APLRRegressor.get_base_predictors_in_each_unique_term_affiliation()
271
+
272
+ def get_term_coefficients(self) -> FloatVector:
273
+ return self.APLRRegressor.get_term_coefficients()
274
+
275
+ def get_validation_error_steps(self) -> FloatMatrix:
276
+ return self.APLRRegressor.get_validation_error_steps()
277
+
278
+ def get_feature_importance(self) -> FloatVector:
279
+ return self.APLRRegressor.get_feature_importance()
280
+
281
+ def get_term_importance(self) -> FloatVector:
282
+ return self.APLRRegressor.get_term_importance()
283
+
284
+ def get_term_main_predictor_indexes(self) -> IntVector:
285
+ return self.APLRRegressor.get_term_main_predictor_indexes()
286
+
287
+ def get_term_interaction_levels(self) -> IntVector:
288
+ return self.APLRRegressor.get_term_interaction_levels()
289
+
290
+ def get_intercept(self) -> float:
291
+ return self.APLRRegressor.get_intercept()
292
+
293
+ def get_optimal_m(self) -> int:
294
+ return self.APLRRegressor.get_optimal_m()
295
+
296
+ def get_validation_tuning_metric(self) -> str:
297
+ return self.APLRRegressor.get_validation_tuning_metric()
298
+
299
+ def get_main_effect_shape(self, predictor_index: int) -> Dict[float, float]:
300
+ return self.APLRRegressor.get_main_effect_shape(predictor_index)
301
+
302
+ def get_unique_term_affiliation_shape(
303
+ self, unique_term_affiliation: str, max_rows_before_sampling: int = 100000
304
+ ) -> FloatMatrix:
305
+ return self.APLRRegressor.get_unique_term_affiliation_shape(
306
+ unique_term_affiliation, max_rows_before_sampling
307
+ )
308
+
309
+ def get_cv_error(self) -> float:
310
+ return self.APLRRegressor.get_cv_error()
311
+
312
+ def set_intercept(self, value: float):
313
+ self.APLRRegressor.set_intercept(value)
314
+
315
+ # For sklearn
316
+ def get_params(self, deep=True):
317
+ return {
318
+ "m": self.m,
319
+ "v": self.v,
320
+ "random_state": self.random_state,
321
+ "loss_function": self.loss_function,
322
+ "link_function": self.link_function,
323
+ "n_jobs": self.n_jobs,
324
+ "cv_folds": self.cv_folds,
325
+ "bins": self.bins,
326
+ "max_interaction_level": self.max_interaction_level,
327
+ "max_interactions": self.max_interactions,
328
+ "verbosity": self.verbosity,
329
+ "min_observations_in_split": self.min_observations_in_split,
330
+ "ineligible_boosting_steps_added": self.ineligible_boosting_steps_added,
331
+ "max_eligible_terms": self.max_eligible_terms,
332
+ "dispersion_parameter": self.dispersion_parameter,
333
+ "validation_tuning_metric": self.validation_tuning_metric,
334
+ "quantile": self.quantile,
335
+ "calculate_custom_validation_error_function": self.calculate_custom_validation_error_function,
336
+ "calculate_custom_loss_function": self.calculate_custom_loss_function,
337
+ "calculate_custom_negative_gradient_function": self.calculate_custom_negative_gradient_function,
338
+ "calculate_custom_transform_linear_predictor_to_predictions_function": self.calculate_custom_transform_linear_predictor_to_predictions_function,
339
+ "calculate_custom_differentiate_predictions_wrt_linear_predictor_function": self.calculate_custom_differentiate_predictions_wrt_linear_predictor_function,
340
+ "boosting_steps_before_interactions_are_allowed": self.boosting_steps_before_interactions_are_allowed,
341
+ "monotonic_constraints_ignore_interactions": self.monotonic_constraints_ignore_interactions,
342
+ "group_mse_by_prediction_bins": self.group_mse_by_prediction_bins,
343
+ "group_mse_cycle_min_obs_in_bin": self.group_mse_cycle_min_obs_in_bin,
344
+ "early_stopping_rounds": self.early_stopping_rounds,
345
+ "num_first_steps_with_linear_effects_only": self.num_first_steps_with_linear_effects_only,
346
+ "penalty_for_non_linearity": self.penalty_for_non_linearity,
347
+ "penalty_for_interactions": self.penalty_for_interactions,
348
+ "max_terms": self.max_terms,
349
+ "ridge_penalty": self.ridge_penalty,
350
+ }
351
+
352
+ # For sklearn
353
+ def set_params(self, **parameters):
354
+ for parameter, value in parameters.items():
355
+ setattr(self, parameter, value)
356
+ self.__set_params_cpp()
357
+ return self
358
+
359
+
360
+ class APLRClassifier:
361
+ def __init__(
362
+ self,
363
+ m: int = 3000,
364
+ v: float = 0.5,
365
+ random_state: int = 0,
366
+ n_jobs: int = 0,
367
+ cv_folds: int = 5,
368
+ bins: int = 300,
369
+ verbosity: int = 0,
370
+ max_interaction_level: int = 1,
371
+ max_interactions: int = 100000,
372
+ min_observations_in_split: int = 4,
373
+ ineligible_boosting_steps_added: int = 15,
374
+ max_eligible_terms: int = 7,
375
+ boosting_steps_before_interactions_are_allowed: int = 0,
376
+ monotonic_constraints_ignore_interactions: bool = False,
377
+ early_stopping_rounds: int = 200,
378
+ num_first_steps_with_linear_effects_only: int = 0,
379
+ penalty_for_non_linearity: float = 0.0,
380
+ penalty_for_interactions: float = 0.0,
381
+ max_terms: int = 0,
382
+ ridge_penalty: float = 0.0001,
383
+ ):
384
+ self.m = m
385
+ self.v = v
386
+ self.random_state = random_state
387
+ self.n_jobs = n_jobs
388
+ self.cv_folds = cv_folds
389
+ self.bins = bins
390
+ self.verbosity = verbosity
391
+ self.max_interaction_level = max_interaction_level
392
+ self.max_interactions = max_interactions
393
+ self.min_observations_in_split = min_observations_in_split
394
+ self.ineligible_boosting_steps_added = ineligible_boosting_steps_added
395
+ self.max_eligible_terms = max_eligible_terms
396
+ self.boosting_steps_before_interactions_are_allowed = (
397
+ boosting_steps_before_interactions_are_allowed
398
+ )
399
+ self.monotonic_constraints_ignore_interactions = (
400
+ monotonic_constraints_ignore_interactions
401
+ )
402
+ self.early_stopping_rounds = early_stopping_rounds
403
+ self.num_first_steps_with_linear_effects_only = (
404
+ num_first_steps_with_linear_effects_only
405
+ )
406
+ self.penalty_for_non_linearity = penalty_for_non_linearity
407
+ self.penalty_for_interactions = penalty_for_interactions
408
+ self.max_terms = max_terms
409
+ self.ridge_penalty = ridge_penalty
410
+
411
+ # Creating aplr_cpp and setting parameters
412
+ self.APLRClassifier = aplr_cpp.APLRClassifier()
413
+ self.__set_params_cpp()
414
+
415
+ # Sets parameters for aplr_cpp.APLRClassifier cpp object
416
+ def __set_params_cpp(self):
417
+ self.APLRClassifier.m = self.m
418
+ self.APLRClassifier.v = self.v
419
+ self.APLRClassifier.random_state = self.random_state
420
+ self.APLRClassifier.n_jobs = self.n_jobs
421
+ self.APLRClassifier.cv_folds = self.cv_folds
422
+ self.APLRClassifier.bins = self.bins
423
+ self.APLRClassifier.verbosity = self.verbosity
424
+ self.APLRClassifier.max_interaction_level = self.max_interaction_level
425
+ self.APLRClassifier.max_interactions = self.max_interactions
426
+ self.APLRClassifier.min_observations_in_split = self.min_observations_in_split
427
+ self.APLRClassifier.ineligible_boosting_steps_added = (
428
+ self.ineligible_boosting_steps_added
429
+ )
430
+ self.APLRClassifier.max_eligible_terms = self.max_eligible_terms
431
+ self.APLRClassifier.boosting_steps_before_interactions_are_allowed = (
432
+ self.boosting_steps_before_interactions_are_allowed
433
+ )
434
+ self.APLRClassifier.monotonic_constraints_ignore_interactions = (
435
+ self.monotonic_constraints_ignore_interactions
436
+ )
437
+ self.APLRClassifier.early_stopping_rounds = self.early_stopping_rounds
438
+ self.APLRClassifier.num_first_steps_with_linear_effects_only = (
439
+ self.num_first_steps_with_linear_effects_only
440
+ )
441
+ self.APLRClassifier.penalty_for_non_linearity = self.penalty_for_non_linearity
442
+ self.APLRClassifier.penalty_for_interactions = self.penalty_for_interactions
443
+ self.APLRClassifier.max_terms = self.max_terms
444
+ self.APLRClassifier.ridge_penalty = self.ridge_penalty
445
+
446
+ def fit(
447
+ self,
448
+ X: FloatMatrix,
449
+ y: List[str],
450
+ sample_weight: FloatVector = np.empty(0),
451
+ X_names: List[str] = [],
452
+ cv_observations: IntMatrix = np.empty([0, 0]),
453
+ prioritized_predictors_indexes: List[int] = [],
454
+ monotonic_constraints: List[int] = [],
455
+ interaction_constraints: List[List[int]] = [],
456
+ predictor_learning_rates: List[float] = [],
457
+ predictor_penalties_for_non_linearity: List[float] = [],
458
+ predictor_penalties_for_interactions: List[float] = [],
459
+ predictor_min_observations_in_split: List[int] = [],
460
+ ):
461
+ self.__set_params_cpp()
462
+ self.APLRClassifier.fit(
463
+ X,
464
+ y,
465
+ sample_weight,
466
+ X_names,
467
+ cv_observations,
468
+ prioritized_predictors_indexes,
469
+ monotonic_constraints,
470
+ interaction_constraints,
471
+ predictor_learning_rates,
472
+ predictor_penalties_for_non_linearity,
473
+ predictor_penalties_for_interactions,
474
+ predictor_min_observations_in_split,
475
+ )
476
+ # For sklearn
477
+ self.classes_ = np.arange(len(self.APLRClassifier.get_categories()))
478
+
479
+ def predict_class_probabilities(
480
+ self, X: FloatMatrix, cap_predictions_to_minmax_in_training: bool = False
481
+ ) -> FloatMatrix:
482
+ return self.APLRClassifier.predict_class_probabilities(
483
+ X, cap_predictions_to_minmax_in_training
484
+ )
485
+
486
+ def predict(
487
+ self, X: FloatMatrix, cap_predictions_to_minmax_in_training: bool = False
488
+ ) -> List[str]:
489
+ return self.APLRClassifier.predict(X, cap_predictions_to_minmax_in_training)
490
+
491
+ def calculate_local_feature_contribution(self, X: FloatMatrix) -> FloatMatrix:
492
+ return self.APLRClassifier.calculate_local_feature_contribution(X)
493
+
494
+ def get_categories(self) -> List[str]:
495
+ return self.APLRClassifier.get_categories()
496
+
497
+ def get_logit_model(self, category: str) -> APLRRegressor:
498
+ return self.APLRClassifier.get_logit_model(category)
499
+
500
+ def get_validation_error_steps(self) -> FloatMatrix:
501
+ return self.APLRClassifier.get_validation_error_steps()
502
+
503
+ def get_cv_error(self) -> float:
504
+ return self.APLRClassifier.get_cv_error()
505
+
506
+ def get_feature_importance(self) -> FloatVector:
507
+ return self.APLRClassifier.get_feature_importance()
508
+
509
+ def get_unique_term_affiliations(self) -> List[str]:
510
+ return self.APLRClassifier.get_unique_term_affiliations()
511
+
512
+ def get_base_predictors_in_each_unique_term_affiliation(self) -> List[List[int]]:
513
+ return self.APLRClassifier.get_base_predictors_in_each_unique_term_affiliation()
514
+
515
+ # For sklearn
516
+ def get_params(self, deep=True):
517
+ return {
518
+ "m": self.m,
519
+ "v": self.v,
520
+ "random_state": self.random_state,
521
+ "n_jobs": self.n_jobs,
522
+ "cv_folds": self.cv_folds,
523
+ "bins": self.bins,
524
+ "verbosity": self.verbosity,
525
+ "max_interaction_level": self.max_interaction_level,
526
+ "max_interactions": self.max_interactions,
527
+ "min_observations_in_split": self.min_observations_in_split,
528
+ "ineligible_boosting_steps_added": self.ineligible_boosting_steps_added,
529
+ "max_eligible_terms": self.max_eligible_terms,
530
+ "boosting_steps_before_interactions_are_allowed": self.boosting_steps_before_interactions_are_allowed,
531
+ "monotonic_constraints_ignore_interactions": self.monotonic_constraints_ignore_interactions,
532
+ "early_stopping_rounds": self.early_stopping_rounds,
533
+ "num_first_steps_with_linear_effects_only": self.num_first_steps_with_linear_effects_only,
534
+ "penalty_for_non_linearity": self.penalty_for_non_linearity,
535
+ "penalty_for_interactions": self.penalty_for_interactions,
536
+ "max_terms": self.max_terms,
537
+ "ridge_penalty": self.ridge_penalty,
538
+ }
539
+
540
+ # For sklearn
541
+ def set_params(self, **parameters):
542
+ for parameter, value in parameters.items():
543
+ setattr(self, parameter, value)
544
+ self.__set_params_cpp()
545
+ return self
546
+
547
+ # For sklearn
548
+ def predict_proba(self, X: FloatMatrix) -> FloatMatrix:
549
+ return self.predict_class_probabilities(X)
550
+
551
+
552
+ class APLRTuner:
553
+ def __init__(
554
+ self,
555
+ parameters: Union[Dict[str, List[float]], List[Dict[str, List[float]]]] = {
556
+ "max_interaction_level": [0, 1],
557
+ "min_observations_in_split": [4, 10, 20, 100, 500, 1000],
558
+ },
559
+ is_regressor: bool = True,
560
+ ):
561
+ self.parameters = parameters
562
+ self.is_regressor = is_regressor
563
+ self.parameter_grid = self._create_parameter_grid()
564
+
565
+ def _create_parameter_grid(self) -> List[Dict[str, float]]:
566
+ items = sorted(self.parameters.items())
567
+ keys, values = zip(*items)
568
+ combinations = list(itertools.product(*values))
569
+ grid = [dict(zip(keys, combination)) for combination in combinations]
570
+ return grid
571
+
572
+ def fit(self, X: FloatMatrix, y: FloatVector, **kwargs):
573
+ self.cv_results: List[Dict[str, float]] = []
574
+ best_validation_result = np.inf
575
+ for params in self.parameter_grid:
576
+ if self.is_regressor:
577
+ model = APLRRegressor(**params)
578
+ else:
579
+ model = APLRClassifier(**params)
580
+ model.fit(X, y, **kwargs)
581
+ cv_error_for_this_model = model.get_cv_error()
582
+ cv_results_for_this_model = model.get_params()
583
+ cv_results_for_this_model["cv_error"] = cv_error_for_this_model
584
+ self.cv_results.append(cv_results_for_this_model)
585
+ if cv_error_for_this_model < best_validation_result:
586
+ best_validation_result = cv_error_for_this_model
587
+ self.best_model = model
588
+ self.cv_results = sorted(self.cv_results, key=lambda x: x["cv_error"])
589
+
590
+ def predict(self, X: FloatMatrix, **kwargs) -> Union[FloatVector, List[str]]:
591
+ return self.best_model.predict(X, **kwargs)
592
+
593
+ def predict_class_probabilities(self, X: FloatMatrix, **kwargs) -> FloatMatrix:
594
+ if self.is_regressor == False:
595
+ return self.best_model.predict_class_probabilities(X, **kwargs)
596
+ else:
597
+ raise TypeError(
598
+ "predict_class_probabilities is only possible when is_regressor is False"
599
+ )
600
+
601
+ def predict_proba(self, X: FloatMatrix, **kwargs) -> FloatMatrix:
602
+ return self.predict_class_probabilities(X, **kwargs)
603
+
604
+ def get_best_estimator(self) -> Union[APLRClassifier, APLRRegressor]:
605
+ return self.best_model
606
+
607
+ def get_cv_results(self) -> List[Dict[str, float]]:
608
+ return self.cv_results
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.4
2
+ Name: aplr
3
+ Version: 10.9.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
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: license
24
+ Dynamic: license-file
25
+ Dynamic: platform
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary
29
+
30
+ # APLR
31
+ **Automatic Piecewise Linear Regression**
32
+
33
+ ## About
34
+ 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.
35
+
36
+ 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.
37
+
38
+ ## Installation
39
+ To install APLR, use the following command:
40
+
41
+ ```bash
42
+ pip install aplr
43
+ ```
44
+
45
+ ## Availability
46
+ APLR is available for Windows, most Linux distributions, and macOS.
47
+
48
+ ## Usage
49
+ Example Python scripts are available [here](https://github.com/ottenbreit-data-science/aplr/tree/main/examples).
50
+
51
+ ## Sponsorship
52
+ Consider sponsoring Von Ottenbreit Data Science by clicking the **Sponsor** button on the repository. Sufficient funding will help maintain and further develop APLR.
53
+
54
+ ## API Reference
55
+ - [API reference for regression](https://github.com/ottenbreit-data-science/aplr/blob/main/API_REFERENCE_FOR_REGRESSION.md)
56
+ - [API reference for classification](https://github.com/ottenbreit-data-science/aplr/blob/main/API_REFERENCE_FOR_CLASSIFICATION.md)
57
+
58
+ ## Contact Information
59
+ For inquiries, please email: [ottenbreitdatascience@gmail.com](mailto:ottenbreitdatascience@gmail.com)
@@ -0,0 +1,8 @@
1
+ aplr_cpp.cpython-311-darwin.so,sha256=xCE7U-xCzGrAQlr1IUFdd7DCR4_j8voC68bSGU5_rYA,1245712
2
+ aplr-10.9.0.dist-info/RECORD,,
3
+ aplr-10.9.0.dist-info/WHEEL,sha256=4yRL7i34iDMjEUoqD8VLw6c4rQ6B4MrCGI-TyFdAJTU,110
4
+ aplr-10.9.0.dist-info/top_level.txt,sha256=DXVC0RIFGpzVnPeKWAZTXQdJheOEZL51Wip6Fx7zbR4,14
5
+ aplr-10.9.0.dist-info/METADATA,sha256=YyVcDnXStzsAnm5KWfxaSYgd70qn6IqhqaoLpZI-3IM,2361
6
+ aplr-10.9.0.dist-info/licenses/LICENSE,sha256=g4qcQtkSVPHtGRi3T93DoFCrssvW6ij_emU-2fj_xfY,1113
7
+ aplr/__init__.py,sha256=rRfTgNWnYZlFatyA920lWqBcjwmQUI7FcvEPFUTJgzE,20
8
+ aplr/aplr.py,sha256=18XnQy37U3AApCWESlfKysuHPsl9_LiF2kyubroFr_Q,26718
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp311-cp311-macosx_11_0_x86_64
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Mathias von Ottenbreit <ottenbreitdatascience@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ aplr
2
+ aplr_cpp
Binary file