Start hereQuick start · 6 you’ll reach for daily
Target versions · paceVersions
scikit-learn ≥ 1.5
numpy ≥ 1.26
pandas ≥ 2.0
python ≥ 3.9
scikit-learn moves slowly and predictably — the core
fit/predict/transform
triad hasn’t changed in years. Recent shifts: set_config(transform_output="pandas")
keeps DataFrames flowing through pipelines (1.2+), HistGradientBoosting* is now
competitive with XGBoost/LightGBM, and OneHotEncoder(sparse=...) was renamed to
sparse_output=.... This sheet pins to 1.5+.
Install · configSetup
# Install — core + the usual companions
pip install "scikit-learn>=1.5" numpy pandas scipy
# Conda (uses MKL by default)
conda install -c conda-forge scikit-learn
# Common extras
pip install joblib # parallel I/O for model persistence
pip install xgboost lightgbm catboost # gradient-boosting drop-ins
pip install imbalanced-learn # SMOTE + samplers
pip install scikit-learn-intelex # Intel CPU accelerators
# Use the modern Pandas-aware output (DataFrames in / DataFrames out)
python -c "
from sklearn import set_config
set_config(transform_output='pandas', # transformers return DataFrames
display='diagram') # pretty repr in notebooks
"
Where things liveCommon imports
| from sklearn.pipeline import Pipeline, make_pipeline | Compose preprocess + model into one estimator. |
| from sklearn.compose import ColumnTransformer, make_column_selector | Per-column-group preprocessing. |
| from sklearn.preprocessing import StandardScaler, OneHotEncoder, MinMaxScaler | The everyday transformers. |
| from sklearn.impute import SimpleImputer, KNNImputer | Missing-value handling. |
| from sklearn.model_selection import train_test_split, StratifiedKFold, GridSearchCV | Splitters + tuners. |
| from sklearn.linear_model import LogisticRegression, Ridge, Lasso, ElasticNet | Linear models. |
| from sklearn.ensemble import RandomForestClassifier, HistGradientBoostingClassifier | Tree ensembles — the modern defaults. |
| from sklearn.metrics import classification_report, roc_auc_score, mean_squared_error | Evaluation. |
| from sklearn import set_config | Global config — pandas output, diagram repr. |
| import joblib | Persistence + parallel utilities. |
fit · predict · transformThe estimator API
| est.fit(X, y) / est.fit(X) | Train. Supervised takes y; unsupervised doesn’t. |
| est.predict(X) | Hard predictions (classifiers + regressors). |
| est.predict_proba(X) / est.decision_function(X) | Soft scores. Not every classifier has both. |
| est.transform(X) / est.fit_transform(X) | Transformers only. Returns the transformed features. |
| est.score(X, y) | Default metric for the estimator (R^2 / accuracy). |
| est.get_params() / est.set_params(**kw) | Hyperparameters as a dict. |
| est.classes_ / est.feature_names_in_ / est.n_features_in_ | Trailing-underscore attributes are learned at fit time. |
| est.feature_importances_ / est.coef_ / est.intercept_ | Model-specific introspection. |
| clone(est) | Make an unfitted copy — useful for cross-val loops. |
Datasets · splittersData, splits, CV
| from sklearn.datasets import load_iris, fetch_openml, make_classification | Bundled / cached / synthetic data. |
| train_test_split(X, y, test_size=0.2, stratify=y, random_state=42) | Stratify on y when classes are imbalanced. |
| KFold(n_splits=5, shuffle=True, random_state=42) | Plain k-fold — regression. |
| StratifiedKFold(...) | Preferred CV for classification. Preserves class balance per fold. |
| GroupKFold / StratifiedGroupKFold | Keep rows of the same group together (e.g. same user). |
| TimeSeriesSplit(n_splits=5) | Forward-only walk-forward splits. Use for time series. |
| cross_val_score(est, X, y, cv=5, scoring="roc_auc", n_jobs=-1) | Quick CV score. |
| cross_validate(est, ..., return_estimator=True) | Multiple metrics + per-fold fitted estimators. |
| cross_val_predict(est, X, y, cv=5, method="predict_proba") | Out-of-fold predictions. |
Scale · encode · imputePreprocessing
| StandardScaler() | Zero mean, unit variance. Preferred for linear / SVM / NN inputs. |
| MinMaxScaler(feature_range=(0,1)) / MaxAbsScaler() | Bounded scaling. |
| RobustScaler() | Median + IQR scaling — resists outliers. |
| PowerTransformer(method="yeo-johnson") | Make distributions more Gaussian. Works on negative values. |
| QuantileTransformer(output_distribution="normal") | Non-linear rank-based transform. |
| OneHotEncoder(handle_unknown="ignore", sparse_output=False) | Categorical → dummies. ignore survives unseen categories. |
| OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1) | Label-encode, robust to new categories. |
| TargetEncoder(smooth="auto") | Target-mean encoding with leak-safe CV (1.4+). |
| SimpleImputer(strategy="median" | "mean" | "most_frequent" | "constant") | Fill missing. |
| KNNImputer(n_neighbors=5) | Distance-based imputation. |
| FunctionTransformer(np.log1p, validate=False) | Wrap a function into a transformer for pipelines. |
Compose preprocess + modelPipelines & ColumnTransformer
| Pipeline([("scale", StandardScaler()), ("clf", LogisticRegression())]) | Named steps. Last step does predict. |
| make_pipeline(StandardScaler(), LogisticRegression()) | Auto-name steps from class names (snake_case). |
| ColumnTransformer([("num", scaler, num_cols), ("cat", ohe, cat_cols)], remainder="drop") | Per-column-group transformers. |
| make_column_selector(dtype_include="number") | Auto-pick columns by dtype. |
| remainder="passthrough" | Keep unmatched columns untransformed. Default is "drop". |
| FeatureUnion([("pca", PCA()), ("kbest", SelectKBest())]) | Run transformers in parallel, concat outputs. |
| pipe[-1] / pipe.named_steps["clf"] | Reach into a step. |
| pipe.set_params(clf__C=0.5) | Tune a nested param — step__param. |
| pipe.get_feature_names_out() | Names after preprocessing — vital for inspection. |
| set_config(transform_output="pandas") | Transformers return DataFrames, not arrays. Keeps column names alive. |
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
num_cols = ["age", "income"]
cat_cols = ["country", "plan"]
# Per-column-group preprocessing
preprocess = ColumnTransformer(transformers=[
("num", Pipeline([
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler()),
]), num_cols),
("cat", Pipeline([
("impute", SimpleImputer(strategy="most_frequent")),
("ohe", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
]), cat_cols),
], remainder="drop", verbose_feature_names_out=False)
# Full estimator — preprocess + model
clf = Pipeline([
("pre", preprocess),
("model", LogisticRegression(C=1.0, max_iter=1000, class_weight="balanced")),
])
clf.fit(X_train, y_train)
clf.predict_proba(X_test)[:, 1]
# Inspect the final feature names (set_config(transform_output='pandas') helps too)
preprocess.fit(X_train)
preprocess.get_feature_names_out()
Linear · trees · SVM · ...Models
Linear & kernel
| LinearRegression() / Ridge(alpha=1.0) / Lasso(alpha=0.1) / ElasticNet | Regression. Ridge = L2, Lasso = L1, ElasticNet = both. |
| LogisticRegression(C=1.0, penalty="l2", class_weight="balanced", max_iter=1000) | Workhorse classifier. Scale features first. |
| SGDClassifier(loss="log_loss") / SGDRegressor | Stochastic gradient — for very large datasets / streaming. |
| SVC(kernel="rbf", C=1.0, gamma="scale") / SVR | Kernel SVM. Scale features. Slow past ~50k rows. |
| LinearSVC(C=1.0, dual="auto") | Linear-kernel SVM — much faster than SVC(kernel="linear"). |
| KNeighborsClassifier(n_neighbors=5, weights="distance") | k-NN. Scale features. |
| GaussianNB() / MultinomialNB() / BernoulliNB() | Naive Bayes — baselines for text. |
Trees & ensembles
| DecisionTreeClassifier(max_depth=None, min_samples_leaf=1) | Single tree. |
| RandomForestClassifier(n_estimators=300, n_jobs=-1) | Bagged trees — solid default for tabular. |
| ExtraTreesClassifier(...) | More randomized splits than RF. |
| HistGradientBoostingClassifier(learning_rate=0.1, max_depth=None) | Native sklearn boosting. Handles NaN. Preferred over deprecated GradientBoostingClassifier. |
| VotingClassifier(estimators=[...], voting="soft") | Average or vote across models. |
| StackingClassifier(estimators=[...], final_estimator=...) | Meta-learner on top of base models. CV-aware. |
Unsupervised
| KMeans(n_clusters=8, n_init="auto") | Clustering. Scale features; tune n_clusters. |
| DBSCAN(eps=0.5, min_samples=5) | Density-based. Finds noise points + variable cluster sizes. |
| AgglomerativeClustering(n_clusters=None, distance_threshold=1.0) | Hierarchical. |
| PCA(n_components=0.95) | Keep 95% of variance. Linear dim-reduction. |
| TruncatedSVD(n_components=100) | PCA-like for sparse matrices (TF-IDF). |
| IsolationForest(contamination="auto") | Anomaly detection by tree isolation. |
CV · grid · halvingModel selection & tuning
| GridSearchCV(est, param_grid={...}, cv=cv, scoring="roc_auc", n_jobs=-1) | Exhaustive grid. Refits the best on full train by default. |
| RandomizedSearchCV(est, param_distributions={...}, n_iter=50) | Sample the space. Preferred over Grid past 4 hyperparams. |
| HalvingGridSearchCV / HalvingRandomSearchCV | Successive-halving — many configs on small data, few on full. |
| param_grid={"clf__C": [0.1, 1, 10]} | Pipeline params use step__param syntax. |
| scoring=["roc_auc", "f1"] / scoring={"auc": "roc_auc"} | Multi-metric. Need refit="auc" when using a dict. |
| gs.best_estimator_ / best_params_ / cv_results_ | Outputs after fit. |
| learning_curve(est, X, y, train_sizes=...) | Diagnose underfit vs overfit. |
| validation_curve(est, X, y, "C", [0.01, 0.1, 1, 10]) | Score vs single hyperparam. |
| permutation_importance(est, X, y, n_repeats=10) | Model-agnostic feature importance. |
from sklearn.model_selection import (
train_test_split, StratifiedKFold, GridSearchCV, cross_val_score
)
from sklearn.metrics import classification_report
# 1. Train/test split — stratify keeps class balance
X_tr, X_te, y_tr, y_te = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42,
)
# 2. K-fold with stratification — the right default for classification
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 3. Cheap baseline: cross-val score of a single model
scores = cross_val_score(clf, X_tr, y_tr, cv=cv, scoring="f1", n_jobs=-1)
print(f"CV f1 = {scores.mean():.3f} ± {scores.std():.3f}")
# 4. Tune hyperparameters — search over the whole Pipeline namespace
grid = {
"pre__num__scale__with_mean": [True, False], # double underscore = step__param
"model__C": [0.1, 1.0, 10.0],
"model__class_weight": [None, "balanced"],
}
gs = GridSearchCV(
clf, param_grid=grid, cv=cv, scoring="roc_auc",
n_jobs=-1, refit=True, verbose=1,
)
gs.fit(X_tr, y_tr)
print("best:", gs.best_params_, gs.best_score_)
# 5. Held-out evaluation
print(classification_report(y_te, gs.predict(X_te)))
Classification · regressionMetrics
| accuracy_score(y, y_hat) | Fraction correct. Misleading on imbalanced data. |
| precision_score / recall_score / f1_score(..., average="macro") | Multi-class: macro averages classes, weighted weights by support. |
| roc_auc_score(y, proba) / average_precision_score(y, proba) | ROC AUC + PR AUC. Preferred for imbalanced classification. |
| classification_report(y, y_hat, digits=3) | Per-class precision / recall / f1 / support. |
| confusion_matrix(y, y_hat) / ConfusionMatrixDisplay.from_predictions(...) | Matrix + matplotlib display. |
| log_loss(y, proba) / brier_score_loss(y, proba) | Probabilistic loss / calibration. |
| mean_squared_error(y, y_hat, squared=False) | RMSE. squared=False returns root. |
| mean_absolute_error / mean_absolute_percentage_error / r2_score | MAE / MAPE / R². |
| make_scorer(my_fn, greater_is_better=True) | Wrap a custom metric for scoring=. |
Calibration · PDP · importanceCalibration & inspection
| CalibratedClassifierCV(clf, method="isotonic", cv=5) | Recalibrate probabilities post-fit. |
| CalibrationDisplay.from_estimator(clf, X, y) | Reliability diagram. |
| PartialDependenceDisplay.from_estimator(clf, X, features=[...]) | PDP / ICE plots. |
| permutation_importance(...).importances_mean | Drop in score when a feature is shuffled. |
| RocCurveDisplay.from_estimator(clf, X, y) / PrecisionRecallDisplay | One-liner ROC / PR plots. |
| LearningCurveDisplay.from_estimator(...) | Train vs val score vs train size. |
joblib · ONNX · skopsPersistence & deployment
| joblib.dump(est, "model.joblib", compress=3) | Standard serialization. Version-sensitive — pin the sklearn version. |
| joblib.load("model.joblib") | Load. Trust the source — pickle executes code. |
| skops.io.dump(est, "model.skops") / skops.io.load(..., trusted=[...]) | Safer alternative — signed, no arbitrary code execution. |
| from skl2onnx import to_onnx; to_onnx(est, X[:1]) | Export to ONNX for cross-language inference. |
| pickle protocol matches the training Python version | Always serialize + load with the same Python + sklearn versions. |
Preprocess · tune · persistEnd-to-end · Adult-income classifier
Full pipeline — ColumnTransformer, HistGradientBoosting, RandomizedSearchCV, joblib save. Drop-in template for tabular classification.
# End-to-end: load → split → preprocess + model in a Pipeline → tune → persist.
import joblib
import pandas as pd
from sklearn.datasets import fetch_openml
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.model_selection import StratifiedKFold, RandomizedSearchCV
from sklearn.metrics import classification_report
df = fetch_openml("adult", version=2, as_frame=True).frame
y = (df.pop("class") == ">50K").astype(int)
X = df
num = X.select_dtypes("number").columns.tolist()
cat = X.select_dtypes(exclude="number").columns.tolist()
pre = ColumnTransformer([
("num", Pipeline([("imp", SimpleImputer("median")),
("sc", StandardScaler())]), num),
("cat", Pipeline([("imp", SimpleImputer("most_frequent")),
("ohe", OneHotEncoder(handle_unknown="ignore"))]), cat),
])
pipe = Pipeline([("pre", pre),
("clf", HistGradientBoostingClassifier(random_state=0))])
search = RandomizedSearchCV(
pipe,
{"clf__learning_rate": [0.05, 0.1, 0.2],
"clf__max_depth": [None, 4, 6, 8],
"clf__l2_regularization": [0, 0.1, 1.0]},
n_iter=12, scoring="roc_auc", n_jobs=-1, random_state=0,
cv=StratifiedKFold(5, shuffle=True, random_state=0),
)
search.fit(X, y)
print(classification_report(y, search.predict(X)))
joblib.dump(search.best_estimator_, "model.joblib")
Best practiceGood to know
Pipeline from day one.
Without it you'll fit the scaler on train + test combined and leak data. Pipelines fit on train only and reuse those parameters at predict time — the only correct way.
HistGradientBoosting for tabular data.
Handles NaN natively, no scaling needed, competitive with XGBoost/LightGBM, and ships with sklearn. The classic GradientBoostingClassifier is deprecated for new code.
train_test_split(..., stratify=y) + StratifiedKFold keep class proportions stable — the difference between a useful CV signal and noise on imbalanced data.
Common trapsWatch out for
scaler.fit(X) outside a pipeline contaminates test data with training stats. Always pipe.fit(X_train, y_train); let the pipeline do it.
predict_proba doesn’t mean "well-calibrated".
Tree / SVM probabilities can be very off — useful for ranking, not for thresholds at face value. Use CalibratedClassifierCV + a reliability diagram.
feature_importances_ can be misleading.
Biased toward high-cardinality features and unrelated to causal effect. Pair with permutation_importance for sanity checking.