Scikit-learn Cheat Sheet
Core scikit-learn workflow covering train/test splitting, pipelines, preprocessing, common estimators, cross-validation, hyperparameter tuning, and evaluation metrics.
Train/Test Split & Fit
Standard supervised learning workflow.
from sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerfrom sklearn.linear_model import LogisticRegressionX_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y)scaler = StandardScaler()X_train = scaler.fit_transform(X_train) # fit + transform on train onlyX_test = scaler.transform(X_test) # transform only on testmodel = LogisticRegression(max_iter=1000)model.fit(X_train, y_train)y_pred = model.predict(X_test)print(model.score(X_test, y_test)) # mean accuracy
Pipelines & ColumnTransformer
Chain preprocessing and a model into one estimator.
from sklearn.pipeline import Pipelinefrom sklearn.compose import ColumnTransformerfrom sklearn.preprocessing import StandardScaler, OneHotEncoderfrom sklearn.ensemble import RandomForestClassifierpreprocess = ColumnTransformer([ ("num", StandardScaler(), ["age", "income"]), ("cat", OneHotEncoder(handle_unknown="ignore"), ["city"]),])pipe = Pipeline([ ("preprocess", preprocess), ("clf", RandomForestClassifier(n_estimators=200, random_state=42)),])pipe.fit(X_train, y_train)pipe.predict(X_test)
Common Estimators
Frequently used models by task.
- LogisticRegression- linear classifier for binary/multiclass problems
- RandomForestClassifier / Regressor- ensemble of decision trees, strong baseline
- SVC- support vector classifier, effective on smaller/high-dim data
- KNeighborsClassifier- instance-based classifier using nearest neighbors
- LinearRegression / Ridge / Lasso- linear regression with optional L2/L1 regularization
- KMeans- centroid-based unsupervised clustering
- PCA- dimensionality reduction via principal components
Cross-Validation & GridSearchCV
Evaluate robustly and tune hyperparameters.
from sklearn.model_selection import cross_val_score, GridSearchCVscores = cross_val_score(pipe, X, y, cv=5, scoring="f1_macro")print(scores.mean(), scores.std())param_grid = { "clf__n_estimators": [100, 200, 400], "clf__max_depth": [None, 10, 20],}grid = GridSearchCV(pipe, param_grid, cv=5, scoring="accuracy", n_jobs=-1)grid.fit(X_train, y_train)print(grid.best_params_, grid.best_score_)
Custom Transformers & FeatureUnion
Wrapping arbitrary logic into the scikit-learn estimator interface so it composes with Pipeline, cross_val_score, and GridSearchCV.
from sklearn.base import BaseEstimator, TransformerMixinfrom sklearn.pipeline import FeatureUnion, Pipelineimport numpy as npclass LogTransformer(BaseEstimator, TransformerMixin): def __init__(self, offset=1.0): self.offset = offset def fit(self, X, y=None): return self # nothing to learn, but must return self def transform(self, X): return np.log(X + self.offset)class ClusterDistanceFeatures(BaseEstimator, TransformerMixin): """Adds distance-to-centroid columns as engineered features.""" def __init__(self, n_clusters=5, random_state=42): self.n_clusters = n_clusters self.random_state = random_state def fit(self, X, y=None): from sklearn.cluster import KMeans self.kmeans_ = KMeans(self.n_clusters, random_state=self.random_state, n_init=10).fit(X) return self def transform(self, X): return self.kmeans_.transform(X) # distance to each centroidcombined = FeatureUnion([ ("log", LogTransformer(offset=1.0)), ("cluster_dist", ClusterDistanceFeatures(n_clusters=8)),])pipe = Pipeline([("features", combined)])pipe.fit_transform(X_train)
Handling Imbalanced Classes
class_weight, resampling, and threshold tuning for datasets where the minority class matters most.
from sklearn.linear_model import LogisticRegressionfrom sklearn.metrics import precision_recall_curve, f1_score# 1. Cheapest fix: penalize misclassifying the minority class moreclf = LogisticRegression(class_weight="balanced", max_iter=1000)clf.fit(X_train, y_train)# 2. Custom weights when 'balanced' isn't aggressive enoughclf = LogisticRegression(class_weight={0: 1, 1: 10})# 3. Tune the decision threshold instead of using the default 0.5probs = clf.predict_proba(X_test)[:, 1]precisions, recalls, thresholds = precision_recall_curve(y_test, probs)f1s = 2 * precisions * recalls / (precisions + recalls + 1e-9)best_threshold = thresholds[np.argmax(f1s[:-1])]y_pred_tuned = (probs >= best_threshold).astype(int)# 4. StratifiedKFold keeps class ratios consistent across CV foldsfrom sklearn.model_selection import StratifiedKFold, cross_val_scorecv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)scores = cross_val_score(clf, X, y, cv=cv, scoring="average_precision")
Probability Calibration
Raw predict_proba output from many models is not well-calibrated; CalibratedClassifierCV fixes that when downstream logic relies on probability thresholds.
from sklearn.calibration import CalibratedClassifierCV, calibration_curvefrom sklearn.svm import SVC# SVC's decision_function scores are not probabilities without calibrationbase = SVC(kernel="rbf", probability=False)calibrated = CalibratedClassifierCV(base, method="isotonic", cv=5)calibrated.fit(X_train, y_train)probs = calibrated.predict_proba(X_test)[:, 1]# 'sigmoid' (Platt scaling) needs less data than 'isotonic';# use isotonic only with >~1000 calibration samples# Diagnose calibration quality with a reliability diagramfraction_pos, mean_pred = calibration_curve(y_test, probs, n_bins=10)# a well-calibrated model has fraction_pos ≈ mean_pred at every bin
Custom Scorers & Nested Cross-Validation
make_scorer for business-specific metrics, and nested CV to get an unbiased estimate of GridSearchCV's chosen model.
from sklearn.metrics import make_scorerfrom sklearn.model_selection import GridSearchCV, cross_val_score, KFolddef cost_weighted_score(y_true, y_pred): # false negatives cost 5x more than false positives in this domain fn = ((y_true == 1) & (y_pred == 0)).sum() fp = ((y_true == 0) & (y_pred == 1)).sum() return -(5 * fn + fp) # scorers are 'greater is better' by conventioncustom_scorer = make_scorer(cost_weighted_score)# Naive: tuning hyperparameters and reporting CV score on the SAME folds# leaks information and overstates generalization performance.# Nested CV separates the two: inner loop tunes, outer loop evaluates.inner_cv = KFold(n_splits=3, shuffle=True, random_state=0)outer_cv = KFold(n_splits=5, shuffle=True, random_state=1)search = GridSearchCV(pipe, param_grid, cv=inner_cv, scoring=custom_scorer)nested_scores = cross_val_score(search, X, y, cv=outer_cv, scoring=custom_scorer)print(nested_scores.mean()) # unbiased estimate of the tuned pipeline
Performance & Correctness Gotchas
Issues that only show up once a scikit-learn pipeline moves from a notebook to production data volume.
- Data leakage via global fit- Calling scaler.fit(X) on the full dataset before splitting leaks test-set statistics into training; always fit inside the CV loop via Pipeline, never before train_test_split
- n_jobs=-1 nesting- Setting n_jobs=-1 on both GridSearchCV and the inner estimator (e.g. RandomForestClassifier) oversubscribes CPU cores and can be slower than parallelizing only one layer
- OneHotEncoder unknown categories at inference- Without handle_unknown='ignore', a category seen only at inference time raises instead of degrading gracefully
- Memory blowup from dense OHE- OneHotEncoder(sparse_output=True) (default) keeps high-cardinality categoricals memory-feasible; converting to a dense array with many categories can exhaust RAM
- Pipeline caching for slow preprocessing- Pipeline(steps, memory='/tmp/sklearn_cache') caches fitted transformer outputs so GridSearchCV doesn't re-run expensive preprocessing for every hyperparameter combination
- random_state inconsistency- Forgetting random_state on both the split and the estimator makes results non-reproducible across runs, which silently breaks debugging and A/B comparisons
- predict vs. predict_proba threshold mismatch- .predict() always uses a 0.5 threshold internally; if you tuned a different threshold via predict_proba, you must apply it manually rather than trusting .predict()
Always fit preprocessing steps (scalers, encoders) only on the training fold — wrap them in a Pipeline so cross_val_score and GridSearchCV refit them per fold automatically, avoiding data leakage from the test set.