devnotes.

Resampling Techniques

Resampling changes the training data so that classes become more balanced. Install the library first:

pip install imbalanced-learn

1. Random undersampling

Remove samples from the majority class.

from imblearn.under_sampling import RandomUnderSampler
from collections import Counter

rus = RandomUnderSampler(random_state=42)
X_rus, y_rus = rus.fit_resample(X_train, y_train)
print(Counter(y_train), "->", Counter(y_rus))
ProsCons
Fast, smaller training setThrows away potentially useful data

2. Random oversampling

Duplicate samples from the minority class.

from imblearn.over_sampling import RandomOverSampler

ros = RandomOverSampler(random_state=42)
X_ros, y_ros = ros.fit_resample(X_train, y_train)
print(Counter(y_train), "->", Counter(y_ros))
ProsCons
No information lostExact copies encourage overfitting

3. SMOTE (Synthetic Minority Over-sampling Technique)

Creates new synthetic minority samples by interpolating between a minority point and one of its nearest minority neighbours.

from imblearn.over_sampling import SMOTE, BorderlineSMOTE, ADASYN

smote = SMOTE(sampling_strategy="auto", k_neighbors=5, random_state=42)
X_sm, y_sm = smote.fit_resample(X_train, y_train)
print(Counter(y_train), "->", Counter(y_sm))

Variants:

VariantIdea
BorderlineSMOTEFocus on minority points near the decision boundary
ADASYNGenerate more samples in harder-to-learn regions
SMOTENCHandles mixed numeric and categorical features
SVMSMOTEUses an SVM to find border areas
ProsCons
More diverse than duplicationCan create noisy samples where classes overlap
Often improves recallNeeds numeric, preferably scaled features
Can hurt if the data is very noisy

4. Cleaning methods (undersampling)

  • Tomek links — remove majority points that form a nearest-neighbour pair with a minority point.
  • Edited Nearest Neighbours (ENN) — remove points whose neighbours disagree with their label.
  • NearMiss — keep majority points closest to the minority class.
from imblearn.under_sampling import TomekLinks, EditedNearestNeighbours

X_tl, y_tl = TomekLinks().fit_resample(X_train, y_train)
X_enn, y_enn = EditedNearestNeighbours().fit_resample(X_train, y_train)

5. Combined resampling

Oversample with SMOTE, then clean noisy points.

from imblearn.combine import SMOTEENN, SMOTETomek

X_comb, y_comb = SMOTETomek(random_state=42).fit_resample(X_train, y_train)
print(Counter(y_comb))

The golden rule: resample only the training set

Never resample the validation or test set — it must reflect the real-world class distribution. Resampling before the split or before cross-validation folds leaks synthetic copies of test samples into training.

Use imblearn.pipeline.Pipeline, which applies resampling only during fit.

from imblearn.pipeline import Pipeline as ImbPipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score

pipe = ImbPipeline([
    ("scale", StandardScaler()),
    ("smote", SMOTE(random_state=42)),              # applied only to training folds
    ("clf", RandomForestClassifier(n_estimators=200, random_state=42)),
])

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(pipe, X_train, y_train, cv=cv, scoring="average_precision")
print("PR-AUC per fold:", scores.round(3), "| mean:", scores.mean().round(3))

Choosing a sampling ratio

sampling_strategy controls the target balance:

ValueMeaning
"auto"Balance to 1:1
0.5Minority becomes 50% of the majority size
{0: 4000, 1: 2000}Exact counts per class

You do not always need 1:1; a moderate ratio such as 1:2 often works well.

Resampling Techniques - Imbalanced Data | Data Preparation | DevNotes