Undersampling rebalances a dataset by reducing the number of majority-class examples instead of increasing the minority — the opposite tradeoff from oversampling: no duplication risk, but real information gets discarded.
Random Undersampling
Using the reference dataset (950 majority, 50 minority): random undersampling randomly discards majority-class examples until the classes balance — reducing 950 down to 50, producing a balanced but much smaller 50/50 dataset of only 100 total rows.
from imblearn.under_sampling import RandomUnderSampler
from collections import Counter
rus = RandomUnderSampler(random_state=42)
X_resampled, y_resampled = rus.fit_resample(X_train, y_train)
print(Counter(y_train)) # {0: 950, 1: 50}
print(Counter(y_resampled)) # {0: 50, 1: 50} -- balanced, but 900 majority rows discarded
The Real Risk — Throwing Away Real Information
Discarding 900 out of 950 majority-class rows means losing whatever genuine patterns and diversity existed among those examples — a real, often significant cost, especially when the original dataset wasn't very large to begin with. This tradeoff is exactly why oversampling is often preferred on smaller datasets, while undersampling can be more reasonable when the majority class is so large that even a reduced sample remains substantial.
Smarter Undersampling — Removing the "Easiest" Majority Examples
from imblearn.under_sampling import TomekLinks
# Instead of removing majority examples randomly, remove ones sitting right at
# the boundary with minority examples -- often the most ambiguous, least useful ones
tomek = TomekLinks()
X_resampled, y_resampled = tomek.fit_resample(X_train, y_train)
Tomek Links specifically identifies majority/minority pairs that are each other's nearest neighbor and removes the majority example from each pair — a more targeted removal strategy than pure random undersampling, aiming to clean up ambiguous boundary regions rather than losing information uniformly at random.
Applying It Correctly — Training Data Only
from sklearn.model_selection import train_test_split
from imblearn.under_sampling import RandomUnderSampler
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
rus = RandomUnderSampler(random_state=42)
X_train_resampled, y_train_resampled = rus.fit_resample(X_train, y_train)
model.fit(X_train_resampled, y_train_resampled)
predictions = model.predict(X_test) # untouched, naturally imbalanced test set
Practical Use Cases
- Very large datasets where the majority class has far more examples than actually needed to represent its pattern well
- Combined with class weights or oversampling as part of a broader strategy, rather than used in isolation
Advantages
- Faster training, since the resulting dataset is smaller
- No duplication — every remaining row is a genuine, distinct original example
Limitations
- Discards potentially useful majority-class information, especially costly on smaller datasets
- Random undersampling can, by chance, remove particularly informative majority examples
Common Mistakes
- Undersampling a dataset that's already small, discarding so much data that the model can't learn a reliable majority-class pattern either.
- Undersampling before the train/test split, same leakage risk as oversampling.
Interview Relevance
Q: "When would you choose undersampling over oversampling?" When the majority class is so large that even after significant reduction, plenty of representative data remains — undersampling avoids oversampling's duplication-overfitting risk, but at the direct cost of discarding real data, so it's most reasonable exactly when that cost is affordable.
Practice Question
You have 1 million majority-class rows and 500 minority-class rows. Would undersampling to a perfect 500/500 balance likely be a reasonable choice? Explain your reasoning.