Python Tutorial
Bootstrap Aggregation (Bagging)
Train many models on random resamples of the data and combine them to cut variance and boost accuracy.
The Core Idea
Bagging reduces variance. It draws many bootstrap samples (random samples of the training set, drawn with replacement), trains one model on each, and averages their predictions (regression) or takes a majority vote (classification). Averaging many noisy-but-unbiased models yields a smoother, more accurate estimator.
It works best with high-variance, low-bias base learners such as deep decision trees.
What a Bootstrap Sample Is
import numpy as np
data = np.array([11, 22, 33, 44, 55])
rng = np.random.default_rng(0)
# sample the same size, WITH replacement -> some repeat, some are left out
sample = rng.choice(data, size=len(data), replace=True)
print(sample) # e.g. [55 22 22 44 11]On average about 37% of rows are left out of each sample — these "out-of-bag" rows give a free validation estimate.
BaggingClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier
from sklearn.metrics import accuracy_score
X, y = load_wine(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42)
bag = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=50,
oob_score=True, # evaluate on left-out rows
random_state=42)
bag.fit(X_train, y_train)
print("OOB score:", round(bag.oob_score_, 3))
print("Test acc :", round(accuracy_score(y_test, bag.predict(X_test)), 3))The out-of-bag (OOB) score is a built-in cross-validation-like estimate that needs no separate validation split.
Random Forest: Bagging + Feature Randomness
A Random Forest is bagging applied to decision trees, with an extra twist: each split considers only a random subset of features. That decorrelates the trees and usually beats plain bagging.
from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(
n_estimators=200, max_features="sqrt", random_state=42)
forest.fit(X_train, y_train)
print("Forest acc:", round(accuracy_score(y_test, forest.predict(X_test)), 3))Bagging vs Boosting
| Bagging | Boosting | |
|---|---|---|
| Training | Parallel, independent models | Sequential, each fixes prior errors |
| Mainly reduces | Variance | Bias |
| Base learner | Deep (low-bias) trees | Shallow (weak) trees |
| Examples | Random Forest | AdaBoost, Gradient Boosting, XGBoost |
Best Practices
- Use deep base trees — bagging is meant to tame their variance.
- More estimators help then plateau; 100–300 is a common sweet spot.
- Use the OOB score for quick, honest validation.
- Reach for Random Forest as a strong, low-tuning default on tabular data.
Try It Yourself
Exercise 1: Train a bagging classifier of 50 trees on wine and print the OOB score.
Show solution
from sklearn.datasets import load_wine
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
X, y = load_wine(return_X_y=True)
bag = BaggingClassifier(DecisionTreeClassifier(), n_estimators=50,
oob_score=True, random_state=0).fit(X, y)
print(round(bag.oob_score_, 3))Exercise 2: Does bagging mainly reduce bias or variance?
Show solution
Variance — averaging many high-variance models (like deep trees) smooths out their fluctuations.
📘 Real-World Deep Dive
Knowing <strong>ML Bootstrap Aggregation (scikit-learn)</strong> well is what turns scikit-learn from a curiosity into a daily tool — you'll reach for it in nearly every real project.
Real-Life Scenario
An end-to-end usage of ML Bootstrap Aggregation that you'd actually see in a data pipeline or analytics notebook.
Real-Life Example
import numpy as np
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)
clf = BaggingClassifier(estimator=DecisionTreeClassifier(), n_estimators=50,
random_state=0).fit(X, y)
print("score:", round(clf.score(X, y), 3))Expected Output
(see source)Common mistakes
fitexpects numeric arrays;OneHotEncoder/LabelEncoderare easy to forget for categorical features.- Calling
predicton a model trained on unscaled data and then scaling inputs at inference time silently degrades accuracy. train_test_split(X, y)requires both arrays;train_test_split(X)for unsupervised learning slips past static checkers.- Treating ML Bootstrap Aggregation as a black box without reading the docs — the API has subtle defaults that bite when you scale.
🚀 Performance & Best Practices
- Wrap preprocessing + estimator in a
Pipelinesofit/predictstay reproducible. - Use
joblib/picklefor serialising models, not the entire Python state. - For > 100 k rows, switch to
HistGradientBoostingClassifierorcuML. - When working with scikit-learn, prefer vectorised / batched operations over Python loops.
🧪 Try It Yourself
- Reproduce the snippet on a representative slice of your own data.
- Profile the snippet with
cProfileortimeitand find the single biggest improvement. - Generalise the snippet into a small, reusable function you can drop into future projects.