lazypredict 0.3.0a2__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.
- lazypredict/Supervised.py +577 -0
- lazypredict/TimeSeriesForecasting.py +1549 -0
- lazypredict/__init__.py +24 -0
- lazypredict/_base.py +508 -0
- lazypredict/cli.py +56 -0
- lazypredict/config.py +214 -0
- lazypredict/exceptions.py +30 -0
- lazypredict/integrations/__init__.py +1 -0
- lazypredict/integrations/mlflow.py +34 -0
- lazypredict/metrics.py +167 -0
- lazypredict/preprocessing.py +168 -0
- lazypredict/py.typed +0 -0
- lazypredict/ts_preprocessing.py +196 -0
- lazypredict-0.3.0a2.dist-info/AUTHORS.md +11 -0
- lazypredict-0.3.0a2.dist-info/LICENSE +22 -0
- lazypredict-0.3.0a2.dist-info/METADATA +488 -0
- lazypredict-0.3.0a2.dist-info/RECORD +20 -0
- lazypredict-0.3.0a2.dist-info/WHEEL +5 -0
- lazypredict-0.3.0a2.dist-info/entry_points.txt +2 -0
- lazypredict-0.3.0a2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Supervised Models — LazyClassifier and LazyRegressor for rapid model benchmarking.
|
|
3
|
+
|
|
4
|
+
Provides LazyClassifier and LazyRegressor classes that train multiple
|
|
5
|
+
scikit-learn models with minimal code to quickly identify which algorithms
|
|
6
|
+
perform best on a given dataset.
|
|
7
|
+
"""
|
|
8
|
+
# Author: Shankar Rao Pandala <shankar.pandala@live.com>
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import signal
|
|
12
|
+
from contextlib import contextmanager
|
|
13
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pandas as pd
|
|
17
|
+
from sklearn.base import ClassifierMixin, RegressorMixin
|
|
18
|
+
from sklearn.metrics import (
|
|
19
|
+
accuracy_score,
|
|
20
|
+
balanced_accuracy_score,
|
|
21
|
+
f1_score,
|
|
22
|
+
mean_squared_error,
|
|
23
|
+
precision_score,
|
|
24
|
+
r2_score,
|
|
25
|
+
recall_score,
|
|
26
|
+
roc_auc_score,
|
|
27
|
+
)
|
|
28
|
+
from sklearn.pipeline import Pipeline
|
|
29
|
+
from sklearn.utils import all_estimators
|
|
30
|
+
|
|
31
|
+
# Re-export from submodules for backward compatibility
|
|
32
|
+
from lazypredict._base import ( # noqa: F401
|
|
33
|
+
LazyEstimator,
|
|
34
|
+
_validate_fit_inputs,
|
|
35
|
+
_validate_init_params,
|
|
36
|
+
)
|
|
37
|
+
from lazypredict.config import (
|
|
38
|
+
REMOVED_CLASSIFIERS,
|
|
39
|
+
REMOVED_REGRESSORS,
|
|
40
|
+
VALID_ENCODERS,
|
|
41
|
+
)
|
|
42
|
+
from lazypredict.exceptions import TimeoutException
|
|
43
|
+
from lazypredict.integrations.mlflow import ( # noqa: F401
|
|
44
|
+
MLFLOW_AVAILABLE,
|
|
45
|
+
is_mlflow_tracking_enabled,
|
|
46
|
+
setup_mlflow,
|
|
47
|
+
)
|
|
48
|
+
from lazypredict.metrics import adjusted_rsquared
|
|
49
|
+
from lazypredict.preprocessing import ( # noqa: F401
|
|
50
|
+
CATEGORY_ENCODERS_AVAILABLE,
|
|
51
|
+
build_preprocessor,
|
|
52
|
+
categorical_transformer_high,
|
|
53
|
+
categorical_transformer_low,
|
|
54
|
+
get_card_split,
|
|
55
|
+
get_categorical_encoder,
|
|
56
|
+
numeric_transformer,
|
|
57
|
+
prepare_dataframes,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Module-level logger — users can configure via logging.getLogger("lazypredict")
|
|
61
|
+
logger = logging.getLogger("lazypredict")
|
|
62
|
+
|
|
63
|
+
# Keep private aliases used in the module
|
|
64
|
+
_VALID_ENCODERS = VALID_ENCODERS
|
|
65
|
+
_REMOVED_CLASSIFIERS = REMOVED_CLASSIFIERS
|
|
66
|
+
_REMOVED_REGRESSORS = REMOVED_REGRESSORS
|
|
67
|
+
|
|
68
|
+
# Private aliases for prepare/build used within fit()
|
|
69
|
+
_prepare_dataframes = prepare_dataframes
|
|
70
|
+
_build_preprocessor = build_preprocessor
|
|
71
|
+
|
|
72
|
+
# Optional xgboost
|
|
73
|
+
try:
|
|
74
|
+
import xgboost
|
|
75
|
+
_XGBOOST_AVAILABLE = True
|
|
76
|
+
except ImportError:
|
|
77
|
+
_XGBOOST_AVAILABLE = False
|
|
78
|
+
|
|
79
|
+
# Optional lightgbm
|
|
80
|
+
try:
|
|
81
|
+
import lightgbm
|
|
82
|
+
_LIGHTGBM_AVAILABLE = True
|
|
83
|
+
except ImportError:
|
|
84
|
+
_LIGHTGBM_AVAILABLE = False
|
|
85
|
+
|
|
86
|
+
# Optional catboost
|
|
87
|
+
try:
|
|
88
|
+
import catboost
|
|
89
|
+
_CATBOOST_AVAILABLE = True
|
|
90
|
+
except ImportError:
|
|
91
|
+
_CATBOOST_AVAILABLE = False
|
|
92
|
+
|
|
93
|
+
# Optional perpetual
|
|
94
|
+
try:
|
|
95
|
+
from perpetual import PerpetualBooster
|
|
96
|
+
PERPETUAL_AVAILABLE = True
|
|
97
|
+
except ImportError:
|
|
98
|
+
PERPETUAL_AVAILABLE = False
|
|
99
|
+
|
|
100
|
+
# Intel Extension for Scikit-learn for better performance
|
|
101
|
+
try:
|
|
102
|
+
from sklearnex import patch_sklearn
|
|
103
|
+
patch_sklearn()
|
|
104
|
+
INTEL_EXTENSION_AVAILABLE = True
|
|
105
|
+
except ImportError:
|
|
106
|
+
INTEL_EXTENSION_AVAILABLE = False
|
|
107
|
+
|
|
108
|
+
# Optional MLflow
|
|
109
|
+
try:
|
|
110
|
+
import mlflow
|
|
111
|
+
except ImportError:
|
|
112
|
+
mlflow = None # type: ignore[assignment]
|
|
113
|
+
|
|
114
|
+
# Kept as module-level lists for backward compatibility but built fresh
|
|
115
|
+
CLASSIFIERS: List[Tuple[str, Any]] = [
|
|
116
|
+
est
|
|
117
|
+
for est in all_estimators()
|
|
118
|
+
if issubclass(est[1], ClassifierMixin) and est[0] not in _REMOVED_CLASSIFIERS
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
REGRESSORS: List[Tuple[str, Any]] = [
|
|
122
|
+
est
|
|
123
|
+
for est in all_estimators()
|
|
124
|
+
if issubclass(est[1], RegressorMixin) and est[0] not in _REMOVED_REGRESSORS
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
# Append optional boosting models
|
|
128
|
+
if _XGBOOST_AVAILABLE:
|
|
129
|
+
REGRESSORS.append(("XGBRegressor", xgboost.XGBRegressor))
|
|
130
|
+
CLASSIFIERS.append(("XGBClassifier", xgboost.XGBClassifier))
|
|
131
|
+
|
|
132
|
+
if _LIGHTGBM_AVAILABLE:
|
|
133
|
+
REGRESSORS.append(("LGBMRegressor", lightgbm.LGBMRegressor))
|
|
134
|
+
CLASSIFIERS.append(("LGBMClassifier", lightgbm.LGBMClassifier))
|
|
135
|
+
|
|
136
|
+
if _CATBOOST_AVAILABLE:
|
|
137
|
+
REGRESSORS.append(("CatBoostRegressor", catboost.CatBoostRegressor))
|
|
138
|
+
CLASSIFIERS.append(("CatBoostClassifier", catboost.CatBoostClassifier))
|
|
139
|
+
|
|
140
|
+
if PERPETUAL_AVAILABLE:
|
|
141
|
+
REGRESSORS.append(("PerpetualBooster", PerpetualBooster))
|
|
142
|
+
CLASSIFIERS.append(("PerpetualBooster", PerpetualBooster))
|
|
143
|
+
|
|
144
|
+
# Backward-compatible aliases for removed_ lists
|
|
145
|
+
removed_classifiers = list(_REMOVED_CLASSIFIERS)
|
|
146
|
+
removed_regressors = list(_REMOVED_REGRESSORS)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@contextmanager
|
|
150
|
+
def time_limit(seconds: int):
|
|
151
|
+
"""Context manager to limit execution time of a code block.
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
seconds : int
|
|
156
|
+
Maximum time in seconds for the code block to execute.
|
|
157
|
+
|
|
158
|
+
Raises
|
|
159
|
+
------
|
|
160
|
+
TimeoutException
|
|
161
|
+
If the code block exceeds the time limit.
|
|
162
|
+
"""
|
|
163
|
+
def signal_handler(signum, frame):
|
|
164
|
+
raise TimeoutException(f"Timed out after {seconds} seconds")
|
|
165
|
+
|
|
166
|
+
if hasattr(signal, "SIGALRM"):
|
|
167
|
+
signal.signal(signal.SIGALRM, signal_handler)
|
|
168
|
+
signal.alarm(seconds)
|
|
169
|
+
try:
|
|
170
|
+
yield
|
|
171
|
+
finally:
|
|
172
|
+
signal.alarm(0)
|
|
173
|
+
else:
|
|
174
|
+
yield
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# ---------------------------------------------------------------------------
|
|
178
|
+
# LazyClassifier
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class LazyClassifier(LazyEstimator):
|
|
183
|
+
"""Fit all classification algorithms available in scikit-learn and benchmark them.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
verbose : int, optional (default=0)
|
|
188
|
+
Set to a positive number to enable progress bars and per-model metric output.
|
|
189
|
+
ignore_warnings : bool, optional (default=True)
|
|
190
|
+
When True, warnings and errors from individual models are suppressed.
|
|
191
|
+
custom_metric : callable or None, optional (default=None)
|
|
192
|
+
A function ``f(y_true, y_pred)`` used for additional evaluation.
|
|
193
|
+
predictions : bool, optional (default=False)
|
|
194
|
+
When True, ``fit()`` returns a tuple of (scores, predictions_dataframe).
|
|
195
|
+
random_state : int, optional (default=42)
|
|
196
|
+
Random seed passed to models that accept it.
|
|
197
|
+
classifiers : list or ``"all"``, optional (default="all")
|
|
198
|
+
Specific classifier classes to train, or ``"all"`` for every available one.
|
|
199
|
+
cv : int or None, optional (default=None)
|
|
200
|
+
Number of folds for cross-validation. If None, uses train/test split only.
|
|
201
|
+
timeout : int or float or None, optional (default=None)
|
|
202
|
+
Maximum seconds for each model. Models exceeding this are skipped.
|
|
203
|
+
categorical_encoder : str, optional (default='onehot')
|
|
204
|
+
Encoder for categorical features: ``'onehot'``, ``'ordinal'``,
|
|
205
|
+
``'target'``, or ``'binary'``.
|
|
206
|
+
n_jobs : int, optional (default=-1)
|
|
207
|
+
Number of parallel jobs for cross-validation. -1 uses all processors.
|
|
208
|
+
max_models : int or None, optional (default=None)
|
|
209
|
+
Maximum number of models to train. None means train all.
|
|
210
|
+
progress_callback : callable or None, optional (default=None)
|
|
211
|
+
Callback ``f(model_name, current, total, metrics)`` called after each model.
|
|
212
|
+
use_gpu : bool, optional (default=False)
|
|
213
|
+
When True, enables GPU acceleration for models that support it
|
|
214
|
+
(e.g., XGBoost, LightGBM, CatBoost). When cuML (RAPIDS) is installed,
|
|
215
|
+
GPU-accelerated scikit-learn equivalents are also added automatically.
|
|
216
|
+
Falls back to CPU if CUDA is unavailable.
|
|
217
|
+
|
|
218
|
+
Examples
|
|
219
|
+
--------
|
|
220
|
+
>>> from lazypredict.Supervised import LazyClassifier
|
|
221
|
+
>>> from sklearn.datasets import load_breast_cancer
|
|
222
|
+
>>> from sklearn.model_selection import train_test_split
|
|
223
|
+
>>> data = load_breast_cancer()
|
|
224
|
+
>>> X = data.data
|
|
225
|
+
>>> y = data.target
|
|
226
|
+
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.5, random_state=123)
|
|
227
|
+
>>> clf = LazyClassifier(verbose=0, ignore_warnings=True, custom_metric=None)
|
|
228
|
+
>>> models, predictions = clf.fit(X_train, X_test, y_train, y_test)
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
def __init__(
|
|
232
|
+
self,
|
|
233
|
+
verbose: int = 0,
|
|
234
|
+
ignore_warnings: bool = True,
|
|
235
|
+
custom_metric: Optional[Callable] = None,
|
|
236
|
+
predictions: bool = False,
|
|
237
|
+
random_state: int = 42,
|
|
238
|
+
classifiers: Union[str, List] = "all",
|
|
239
|
+
cv: Optional[int] = None,
|
|
240
|
+
timeout: Optional[Union[int, float]] = None,
|
|
241
|
+
categorical_encoder: str = "onehot",
|
|
242
|
+
n_jobs: int = -1,
|
|
243
|
+
max_models: Optional[int] = None,
|
|
244
|
+
progress_callback: Optional[Callable] = None,
|
|
245
|
+
use_gpu: bool = False,
|
|
246
|
+
):
|
|
247
|
+
super().__init__(
|
|
248
|
+
verbose=verbose,
|
|
249
|
+
ignore_warnings=ignore_warnings,
|
|
250
|
+
custom_metric=custom_metric,
|
|
251
|
+
predictions=predictions,
|
|
252
|
+
random_state=random_state,
|
|
253
|
+
cv=cv,
|
|
254
|
+
timeout=timeout,
|
|
255
|
+
categorical_encoder=categorical_encoder,
|
|
256
|
+
n_jobs=n_jobs,
|
|
257
|
+
max_models=max_models,
|
|
258
|
+
progress_callback=progress_callback,
|
|
259
|
+
use_gpu=use_gpu,
|
|
260
|
+
)
|
|
261
|
+
self.classifiers = classifiers
|
|
262
|
+
|
|
263
|
+
def _estimator_step_name(self) -> str:
|
|
264
|
+
return "classifier"
|
|
265
|
+
|
|
266
|
+
def _get_estimator_list(self) -> List[Tuple[str, Any]]:
|
|
267
|
+
if self.classifiers == "all":
|
|
268
|
+
return list(CLASSIFIERS)
|
|
269
|
+
try:
|
|
270
|
+
return [(cls.__name__, cls) for cls in self.classifiers]
|
|
271
|
+
except Exception as exc:
|
|
272
|
+
logger.error("Invalid classifier(s): %s", exc)
|
|
273
|
+
raise ValueError(f"Invalid classifier(s): {exc}") from exc
|
|
274
|
+
|
|
275
|
+
def _cv_scoring(self) -> Optional[Dict[str, str]]:
|
|
276
|
+
return {
|
|
277
|
+
"accuracy": "accuracy",
|
|
278
|
+
"balanced_accuracy": "balanced_accuracy",
|
|
279
|
+
"f1_weighted": "f1_weighted",
|
|
280
|
+
"precision_weighted": "precision_weighted",
|
|
281
|
+
"recall_weighted": "recall_weighted",
|
|
282
|
+
"roc_auc_ovr_weighted": "roc_auc_ovr_weighted",
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
def _cv_column_names(self) -> List[str]:
|
|
286
|
+
return [
|
|
287
|
+
"Accuracy CV Mean", "Accuracy CV Std",
|
|
288
|
+
"Balanced Accuracy CV Mean", "Balanced Accuracy CV Std",
|
|
289
|
+
"ROC AUC CV Mean", "ROC AUC CV Std",
|
|
290
|
+
"F1 Score CV Mean", "F1 Score CV Std",
|
|
291
|
+
"Precision CV Mean", "Precision CV Std",
|
|
292
|
+
"Recall CV Mean", "Recall CV Std",
|
|
293
|
+
]
|
|
294
|
+
|
|
295
|
+
def _process_cv_results(
|
|
296
|
+
self, cv_results: Dict[str, Any], X_train: pd.DataFrame
|
|
297
|
+
) -> Dict[str, Optional[float]]:
|
|
298
|
+
result: Dict[str, Optional[float]] = {
|
|
299
|
+
"Accuracy CV Mean": cv_results["test_accuracy"].mean(),
|
|
300
|
+
"Accuracy CV Std": cv_results["test_accuracy"].std(),
|
|
301
|
+
"Balanced Accuracy CV Mean": cv_results["test_balanced_accuracy"].mean(),
|
|
302
|
+
"Balanced Accuracy CV Std": cv_results["test_balanced_accuracy"].std(),
|
|
303
|
+
"F1 Score CV Mean": cv_results["test_f1_weighted"].mean(),
|
|
304
|
+
"F1 Score CV Std": cv_results["test_f1_weighted"].std(),
|
|
305
|
+
"Precision CV Mean": cv_results["test_precision_weighted"].mean(),
|
|
306
|
+
"Precision CV Std": cv_results["test_precision_weighted"].std(),
|
|
307
|
+
"Recall CV Mean": cv_results["test_recall_weighted"].mean(),
|
|
308
|
+
"Recall CV Std": cv_results["test_recall_weighted"].std(),
|
|
309
|
+
}
|
|
310
|
+
try:
|
|
311
|
+
result["ROC AUC CV Mean"] = cv_results["test_roc_auc_ovr_weighted"].mean()
|
|
312
|
+
result["ROC AUC CV Std"] = cv_results["test_roc_auc_ovr_weighted"].std()
|
|
313
|
+
except Exception:
|
|
314
|
+
result["ROC AUC CV Mean"] = None
|
|
315
|
+
result["ROC AUC CV Std"] = None
|
|
316
|
+
return result
|
|
317
|
+
|
|
318
|
+
def _compute_metrics(
|
|
319
|
+
self, pipe: Pipeline, X_test: pd.DataFrame, y_test: Any, X_train: pd.DataFrame
|
|
320
|
+
) -> Dict[str, Any]:
|
|
321
|
+
y_pred = pipe.predict(X_test)
|
|
322
|
+
accuracy = accuracy_score(y_test, y_pred, normalize=True)
|
|
323
|
+
b_accuracy = balanced_accuracy_score(y_test, y_pred)
|
|
324
|
+
f1 = f1_score(y_test, y_pred, average="weighted")
|
|
325
|
+
precision = precision_score(y_test, y_pred, average="weighted", zero_division=0)
|
|
326
|
+
recall_val = recall_score(y_test, y_pred, average="weighted")
|
|
327
|
+
|
|
328
|
+
roc_auc = None
|
|
329
|
+
try:
|
|
330
|
+
if hasattr(pipe, "predict_proba"):
|
|
331
|
+
y_pred_proba = pipe.predict_proba(X_test)
|
|
332
|
+
if y_pred_proba.shape[1] == 2:
|
|
333
|
+
roc_auc = roc_auc_score(y_test, y_pred_proba[:, 1])
|
|
334
|
+
else:
|
|
335
|
+
roc_auc = roc_auc_score(
|
|
336
|
+
y_test, y_pred_proba, multi_class="ovr", average="weighted"
|
|
337
|
+
)
|
|
338
|
+
elif hasattr(pipe, "decision_function"):
|
|
339
|
+
roc_auc = roc_auc_score(y_test, pipe.decision_function(X_test))
|
|
340
|
+
else:
|
|
341
|
+
roc_auc = roc_auc_score(y_test, y_pred)
|
|
342
|
+
except Exception as roc_exc:
|
|
343
|
+
if not self.ignore_warnings:
|
|
344
|
+
logger.warning("ROC AUC couldn't be calculated: %s", roc_exc)
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
"accuracy": accuracy,
|
|
348
|
+
"balanced_accuracy": b_accuracy,
|
|
349
|
+
"roc_auc": roc_auc,
|
|
350
|
+
"f1": f1,
|
|
351
|
+
"precision": precision,
|
|
352
|
+
"recall": recall_val,
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
def _build_scores_dataframe(self, results: List[Dict[str, Any]]) -> pd.DataFrame:
|
|
356
|
+
if not results:
|
|
357
|
+
return pd.DataFrame()
|
|
358
|
+
|
|
359
|
+
rows = []
|
|
360
|
+
for r in results:
|
|
361
|
+
row: Dict[str, Any] = {
|
|
362
|
+
"Model": r["name"],
|
|
363
|
+
"Accuracy": r["accuracy"],
|
|
364
|
+
"Balanced Accuracy": r["balanced_accuracy"],
|
|
365
|
+
"ROC AUC": r["roc_auc"],
|
|
366
|
+
"F1 Score": r["f1"],
|
|
367
|
+
"Precision": r["precision"],
|
|
368
|
+
"Recall": r["recall"],
|
|
369
|
+
}
|
|
370
|
+
if self.custom_metric is not None:
|
|
371
|
+
row[self.custom_metric.__name__] = r.get("custom_metric")
|
|
372
|
+
# CV columns
|
|
373
|
+
for col in self._cv_column_names():
|
|
374
|
+
if col in r:
|
|
375
|
+
row[col] = r[col]
|
|
376
|
+
row["Time Taken"] = r["time"]
|
|
377
|
+
rows.append(row)
|
|
378
|
+
|
|
379
|
+
scores = pd.DataFrame(rows)
|
|
380
|
+
scores = scores.sort_values(by="Balanced Accuracy", ascending=False).set_index("Model")
|
|
381
|
+
return scores
|
|
382
|
+
|
|
383
|
+
def _log_verbose(self, name: str, metrics: Dict[str, Any]) -> None:
|
|
384
|
+
logger.info(
|
|
385
|
+
"Model=%s Accuracy=%.4f BalAcc=%.4f ROC_AUC=%s F1=%.4f Time=%.2fs",
|
|
386
|
+
name,
|
|
387
|
+
metrics["accuracy"],
|
|
388
|
+
metrics["balanced_accuracy"],
|
|
389
|
+
metrics["roc_auc"],
|
|
390
|
+
metrics["f1"],
|
|
391
|
+
metrics["time"],
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
# ---------------------------------------------------------------------------
|
|
396
|
+
# LazyRegressor
|
|
397
|
+
# ---------------------------------------------------------------------------
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class LazyRegressor(LazyEstimator):
|
|
401
|
+
"""Fit all regression algorithms available in scikit-learn and benchmark them.
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
verbose : int, optional (default=0)
|
|
406
|
+
Set to a positive number to enable progress bars and per-model metric output.
|
|
407
|
+
ignore_warnings : bool, optional (default=True)
|
|
408
|
+
When True, warnings and errors from individual models are suppressed.
|
|
409
|
+
custom_metric : callable or None, optional (default=None)
|
|
410
|
+
A function ``f(y_true, y_pred)`` used for additional evaluation.
|
|
411
|
+
predictions : bool, optional (default=False)
|
|
412
|
+
When True, ``fit()`` returns a tuple of (scores, predictions_dataframe).
|
|
413
|
+
random_state : int, optional (default=42)
|
|
414
|
+
Random seed passed to models that accept it.
|
|
415
|
+
regressors : list or ``"all"``, optional (default="all")
|
|
416
|
+
Specific regressor classes to train, or ``"all"`` for every available one.
|
|
417
|
+
cv : int or None, optional (default=None)
|
|
418
|
+
Number of folds for cross-validation. If None, uses train/test split only.
|
|
419
|
+
timeout : int or float or None, optional (default=None)
|
|
420
|
+
Maximum seconds for each model. Models exceeding this are skipped.
|
|
421
|
+
categorical_encoder : str, optional (default='onehot')
|
|
422
|
+
Encoder for categorical features: ``'onehot'``, ``'ordinal'``,
|
|
423
|
+
``'target'``, or ``'binary'``.
|
|
424
|
+
n_jobs : int, optional (default=-1)
|
|
425
|
+
Number of parallel jobs for cross-validation. -1 uses all processors.
|
|
426
|
+
max_models : int or None, optional (default=None)
|
|
427
|
+
Maximum number of models to train. None means train all.
|
|
428
|
+
progress_callback : callable or None, optional (default=None)
|
|
429
|
+
Callback ``f(model_name, current, total, metrics)`` called after each model.
|
|
430
|
+
use_gpu : bool, optional (default=False)
|
|
431
|
+
When True, enables GPU acceleration for models that support it
|
|
432
|
+
(e.g., XGBoost, LightGBM). Falls back to CPU if CUDA is unavailable.
|
|
433
|
+
|
|
434
|
+
Examples
|
|
435
|
+
--------
|
|
436
|
+
>>> from lazypredict.Supervised import LazyRegressor
|
|
437
|
+
>>> from sklearn import datasets
|
|
438
|
+
>>> from sklearn.utils import shuffle
|
|
439
|
+
>>> import numpy as np
|
|
440
|
+
>>> diabetes = datasets.load_diabetes()
|
|
441
|
+
>>> X, y = shuffle(diabetes.data, diabetes.target, random_state=13)
|
|
442
|
+
>>> X = X.astype(np.float32)
|
|
443
|
+
>>> offset = int(X.shape[0] * 0.9)
|
|
444
|
+
>>> X_train, y_train = X[:offset], y[:offset]
|
|
445
|
+
>>> X_test, y_test = X[offset:], y[offset:]
|
|
446
|
+
>>> reg = LazyRegressor(verbose=0, ignore_warnings=False, custom_metric=None)
|
|
447
|
+
>>> models, predictions = reg.fit(X_train, X_test, y_train, y_test)
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
def __init__(
|
|
451
|
+
self,
|
|
452
|
+
verbose: int = 0,
|
|
453
|
+
ignore_warnings: bool = True,
|
|
454
|
+
custom_metric: Optional[Callable] = None,
|
|
455
|
+
predictions: bool = False,
|
|
456
|
+
random_state: int = 42,
|
|
457
|
+
regressors: Union[str, List] = "all",
|
|
458
|
+
cv: Optional[int] = None,
|
|
459
|
+
timeout: Optional[Union[int, float]] = None,
|
|
460
|
+
categorical_encoder: str = "onehot",
|
|
461
|
+
n_jobs: int = -1,
|
|
462
|
+
max_models: Optional[int] = None,
|
|
463
|
+
progress_callback: Optional[Callable] = None,
|
|
464
|
+
use_gpu: bool = False,
|
|
465
|
+
):
|
|
466
|
+
super().__init__(
|
|
467
|
+
verbose=verbose,
|
|
468
|
+
ignore_warnings=ignore_warnings,
|
|
469
|
+
custom_metric=custom_metric,
|
|
470
|
+
predictions=predictions,
|
|
471
|
+
random_state=random_state,
|
|
472
|
+
cv=cv,
|
|
473
|
+
timeout=timeout,
|
|
474
|
+
categorical_encoder=categorical_encoder,
|
|
475
|
+
n_jobs=n_jobs,
|
|
476
|
+
max_models=max_models,
|
|
477
|
+
progress_callback=progress_callback,
|
|
478
|
+
use_gpu=use_gpu,
|
|
479
|
+
)
|
|
480
|
+
self.regressors = regressors
|
|
481
|
+
|
|
482
|
+
def _estimator_step_name(self) -> str:
|
|
483
|
+
return "regressor"
|
|
484
|
+
|
|
485
|
+
def _get_estimator_list(self) -> List[Tuple[str, Any]]:
|
|
486
|
+
if self.regressors == "all":
|
|
487
|
+
return list(REGRESSORS)
|
|
488
|
+
try:
|
|
489
|
+
return [(cls.__name__, cls) for cls in self.regressors]
|
|
490
|
+
except Exception as exc:
|
|
491
|
+
logger.error("Invalid regressor(s): %s", exc)
|
|
492
|
+
raise ValueError(f"Invalid regressor(s): {exc}") from exc
|
|
493
|
+
|
|
494
|
+
def _cv_scoring(self) -> Optional[Dict[str, str]]:
|
|
495
|
+
return {
|
|
496
|
+
"r2": "r2",
|
|
497
|
+
"neg_mean_squared_error": "neg_mean_squared_error",
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
def _cv_column_names(self) -> List[str]:
|
|
501
|
+
return [
|
|
502
|
+
"R-Squared CV Mean", "R-Squared CV Std",
|
|
503
|
+
"Adjusted R-Squared CV Mean", "Adjusted R-Squared CV Std",
|
|
504
|
+
"RMSE CV Mean", "RMSE CV Std",
|
|
505
|
+
]
|
|
506
|
+
|
|
507
|
+
def _process_cv_results(
|
|
508
|
+
self, cv_results: Dict[str, Any], X_train: pd.DataFrame
|
|
509
|
+
) -> Dict[str, Optional[float]]:
|
|
510
|
+
rmse_cv = np.sqrt(-cv_results["test_neg_mean_squared_error"])
|
|
511
|
+
adj_r2_cv = [
|
|
512
|
+
adjusted_rsquared(r2, X_train.shape[0], X_train.shape[1])
|
|
513
|
+
for r2 in cv_results["test_r2"]
|
|
514
|
+
]
|
|
515
|
+
return {
|
|
516
|
+
"R-Squared CV Mean": cv_results["test_r2"].mean(),
|
|
517
|
+
"R-Squared CV Std": cv_results["test_r2"].std(),
|
|
518
|
+
"Adjusted R-Squared CV Mean": np.mean(adj_r2_cv),
|
|
519
|
+
"Adjusted R-Squared CV Std": np.std(adj_r2_cv),
|
|
520
|
+
"RMSE CV Mean": rmse_cv.mean(),
|
|
521
|
+
"RMSE CV Std": rmse_cv.std(),
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
def _compute_metrics(
|
|
525
|
+
self, pipe: Pipeline, X_test: pd.DataFrame, y_test: Any, X_train: pd.DataFrame
|
|
526
|
+
) -> Dict[str, Any]:
|
|
527
|
+
y_pred = pipe.predict(X_test)
|
|
528
|
+
r_squared = r2_score(y_test, y_pred)
|
|
529
|
+
adj_rsquared = adjusted_rsquared(
|
|
530
|
+
r_squared, X_test.shape[0], X_test.shape[1]
|
|
531
|
+
)
|
|
532
|
+
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
|
|
533
|
+
return {
|
|
534
|
+
"r_squared": r_squared,
|
|
535
|
+
"adjusted_r_squared": adj_rsquared,
|
|
536
|
+
"rmse": rmse,
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
def _build_scores_dataframe(self, results: List[Dict[str, Any]]) -> pd.DataFrame:
|
|
540
|
+
if not results:
|
|
541
|
+
return pd.DataFrame()
|
|
542
|
+
|
|
543
|
+
rows = []
|
|
544
|
+
for r in results:
|
|
545
|
+
row: Dict[str, Any] = {
|
|
546
|
+
"Model": r["name"],
|
|
547
|
+
"Adjusted R-Squared": r["adjusted_r_squared"],
|
|
548
|
+
"R-Squared": r["r_squared"],
|
|
549
|
+
"RMSE": r["rmse"],
|
|
550
|
+
}
|
|
551
|
+
# CV columns
|
|
552
|
+
for col in self._cv_column_names():
|
|
553
|
+
if col in r:
|
|
554
|
+
row[col] = r[col]
|
|
555
|
+
if self.custom_metric is not None:
|
|
556
|
+
row[self.custom_metric.__name__] = r.get("custom_metric")
|
|
557
|
+
row["Time Taken"] = r["time"]
|
|
558
|
+
rows.append(row)
|
|
559
|
+
|
|
560
|
+
scores = pd.DataFrame(rows)
|
|
561
|
+
scores = scores.sort_values(by="Adjusted R-Squared", ascending=False).set_index("Model")
|
|
562
|
+
return scores
|
|
563
|
+
|
|
564
|
+
def _log_verbose(self, name: str, metrics: Dict[str, Any]) -> None:
|
|
565
|
+
logger.info(
|
|
566
|
+
"Model=%s R2=%.4f AdjR2=%.4f RMSE=%.4f Time=%.2fs",
|
|
567
|
+
name,
|
|
568
|
+
metrics["r_squared"],
|
|
569
|
+
metrics["adjusted_r_squared"],
|
|
570
|
+
metrics["rmse"],
|
|
571
|
+
metrics["time"],
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
# Backward-compatible aliases
|
|
576
|
+
Regression = LazyRegressor
|
|
577
|
+
Classification = LazyClassifier
|