Handling Imbalanced Datasets
Learn to handle imbalanced datasets in Python for data science. Practical steps, troubleshooting, and next-lesson guidance.
Focus: handle imbalanced datasets
You've built a model that scores 97% accuracy, then you check the confusion matrix and realize it predicts the majority class for everything — your 3% of actual positive cases are completely invisible. This is the silent trap of imbalanced datasets, and it's more common than you think: fraud detection, rare disease diagnosis, churn prediction, and manufacturing defect detection all suffer from it. In this lesson, you'll stop relying on deceptive accuracy and learn how to handle imbalanced datasets with resampling, class weights, and proper evaluation metrics — so your model actually learns from the minority class, not just the majority.
The problem this lesson solves
Imagine you're building a fraud detection system for credit card transactions. Out of 100,000 transactions, only 200 are fraudulent. If you build a simple logistic regression without any adjustments, the model quickly figures out it can get 99.8% accuracy by predicting "not fraud" for every single transaction. It looks brilliant on paper, but it's completely useless — you catch zero fraud.
This is the accuracy paradox: in an imbalanced dataset, accuracy becomes a misleading metric because the majority class dominates the prediction. The real problem isn't just the class distribution — it's that most machine learning algorithms are designed to minimize overall error, which naturally biases them toward the majority class.
Unless you actively handle imbalanced datasets, your model will learn a trivial decision boundary that ignores the minority class entirely, no matter how complex your algorithm is.
Core concept / mental model
Think of training a model as teaching a student to spot rare birds in a forest. If you show them 1,000 sparrows and only 10 kingfishers, they'll learn to say "not a kingfisher" for everything and still be 99% correct. To fix this, you have two main strategies:
- Change the data so the student sees more kingfishers (resampling).
- Change the grading so missing a kingfisher costs more than mislabeling a sparrow (class weights or cost-sensitive learning).
In machine learning terms, you have two categories of techniques:
-
Data-level methods: change the class distribution before training. - Oversampling: duplicate or synthesize samples from the minority class (e.g., SMOTE). - Undersampling: remove samples from the majority class.
-
Algorithm-level methods: modify the learning algorithm to penalize mistakes on the minority class more heavily. - Class weights in logistic regression, random forests, or SVM. - Specialized algorithms like BalancedRandomForest.
Here's a mental model to keep in mind: the goal is to force the model to see the minority class as important and to evaluate success with metrics that don't reward the "always predict majority" cheat.
How it works step by step
Step 1: Detect class imbalance
Start by checking the distribution of your target variable:
import pandas as pd
df = pd.read_csv('creditcard.csv')
print(df['Class'].value_counts(normalize=True))
If the minority class is under ~20% of the data, you need to think about imbalance handling. Extreme imbalance (like 1% or less) demands stronger treatment.
Step 2: Choose your strategy
Based on your dataset size, noise level, and model type, pick from the approaches in the comparison table below. Often you'll combine data-level and algorithm-level tricks.
Step 3: Apply the chosen technique
For resampling, use imbalanced-learn (a scikit-learn add-on). For class weights, most scikit-learn models support class_weight='balanced'.
Step 4: Train and evaluate properly
Never rely on accuracy alone. Use confusion matrix, precision, recall, F1-score, and ROC-AUC.
Hands-on walkthrough
Let's walk through a complete example using the classic credit card fraud dataset. We'll compare a baseline model vs. one using class weights.
First, split your data (do this before any resampling to avoid data leakage):
from sklearn.model_selection import train_test_split
X = df.drop('Class', axis=1)
y = df['Class']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
Baseline: no imbalance handling
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train)
print(classification_report(y_test, model.predict(X_test)))
Expected output (simplified):
precision recall f1-score support
0 0.99 1.00 1.00 56861
1 0.00 0.00 0.00 99
The model gets ~99% recall on the majority, but zero true positives. Useless for fraud detection.
Fix 1: class weights
from sklearn.model_selection import cross_val_score
weighted_model = LogisticRegression(max_iter=1000, class_weight='balanced')
weighted_model.fit(X_train, y_train)
print(classification_report(y_test, weighted_model.predict(X_test)))
# Check if it's better than random guessing
print("Cross-val ROC-AUC:", cross_val_score(weighted_model, X_train, y_train, scoring='roc_auc', cv=5).mean())
Fix 2: oversampling with SMOTE
Oversampling happens after the train/test split, on the training data only:
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=42)
X_train_resampled, y_train_resampled = smote.fit_resample(X_train, y_train)
print("Class distribution after SMOTE:", y_train_resampled.value_counts())
smote_model = LogisticRegression(max_iter=1000)
smote_model.fit(X_train_resampled, y_train_resampled)
print(classification_report(y_test, smote_model.predict(X_test)))
Compare options / when to choose what
The table below compares the most common strategies for handling imbalanced datasets:
| Technique | How it works | Pros | Cons | Best for |
|---|---|---|---|---|
| Class weights | Penalize misclassifications of minority class more heavily | Simple, no data change, works with most sklearn models | May overfit if minority is very small | Quick baseline, large datasets |
| Random oversampling | Duplicate random minority samples | Easy, keeps all info | Homogeneous duplicates can overfit | Small datasets with noisy classes |
| SMOTE | Create synthetic minority samples on lines between neighbors | More diverse, often better than duplicate oversampling | Sensitive to noise, can create overlaps | Medium datasets, structured data |
| Random undersampling | Remove random majority samples | Balances dataset, reduces training time | Loses info, may discard important majority | Very large datasets |
When to choose what:
- Extreme imbalance (<1%) → combine SMOTE with class-weight RF or BalancedRandomForest
- Almost800 training hyperparameter tuning for decision trees — you can set
max_depth, and our earlier random forest did oversampling on the training split; the tree uses stratified splits by default. We should do the randomness properly: when next topic is uniform datasets, we can imagine a per-variable view.
Key principles to remember
- Many papers use Tomek links for undersampling (removing noisy edges), and it can be an alternative; but sticking to oversampling + class weights is the simplest robust pattern.
- Resampling should occur inside a pipeline so it doesn't leak test information; that’s covered in the next lesson about imbalanced cross-validation.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.