Feature Scaling in Machine Learning
Press Next → or use ← → arrow keys
Why Feature Scaling Is Essential
StandardScaler, creatinine could finally speak: F1 rose from 0.54 to 0.81.
Scale or Skip? One Simple Test
If the algorithm involves distance, gradient, or regularisation → scale. If it involves tree, split, or threshold → skip.
The Four Core Scalers
All four rescale — but Min-Max & Max-Abs use the extremes (fragile to outliers), Standard uses mean & σ (partly fragile), and Robust uses median & IQR (outlier-proof).
One Value, Four Scalers — Worked
Each method stores a few numbers at fit time — min & max, or mean & σ, or median & IQR, or the max absolute value — then applies the same formula to every row. That's why the fitted scaler is the model of your data's scale.
Min-Max Normalisation → [0, 1]
A fraud-detection network wouldn't converge — transaction amount (₹50–₹4.2M) produced gradients millions of times larger than failed-login counts (0–8). Loss thrashed for 300 epochs. MinMax fixed it: clean convergence in 45.
from sklearn.preprocessing import MinMaxScaler
sc = MinMaxScaler(feature_range=(0,1))
sc.fit(X_train[num]) # train only
X_train[num] = sc.transform(X_train[num])
X_test[num] = sc.transform(X_test[num])
Add a single ₹5,000,000 value and every normal point is crushed into 0.00–0.01. Min-Max only when the column is already outlier-free.
Z-Score Standardisation → μ=0, σ=1
A bank read its loan model's coefficients as credit_score 1.42 vs salary 0.000003 — "473,000× more important." Pure scale artifact. After standardising, salary emerged the strongest predictor (2.3) and all coefficients were finally comparable.
from sklearn.preprocessing import StandardScaler
sc = StandardScaler().fit(X_train[num]) # learns mean_ & var_ from train
X_train[num] = sc.transform(X_train[num]); X_test[num] = sc.transform(X_test[num])
pd.DataFrame(X_train[num]).agg(['mean','std']).round(2) # ≈ 0 and 1 ✅
Robust Scaling — Median & IQR
An insurer's claims ran ₹50k–₹120k normally, with legitimate catastrophes over ₹4.2M. StandardScaler dragged the mean to ₹340k and squashed normal claims near zero (R² 0.71). RobustScaler kept them well-spread (R² 0.84).
from sklearn.preprocessing import RobustScaler
sc = RobustScaler(quantile_range=(25.0,75.0)).fit(X_train[num]) # median + IQR
# the ₹4.2M outlier: StandardScaler ≈ 6.3σ · RobustScaler ≈ 68.6 (stays far, doesn't distort)
Max-Abs & the Full Comparison
| Scaler | Formula | Output | Outlier-proof | Keeps 0s | Best for |
|---|---|---|---|---|---|
| MinMax | (x−min)/(max−min) | [0,1] | no | no | Neural nets, image pixels |
| Standard | (x−μ)/σ | (−∞,∞) | partial | no | Linear, PCA, SVM |
| Robust | (x−Q2)/IQR | (−∞,∞) | yes | no | Legit extreme values |
| MaxAbs | x/|max| | [−1,1] | no | yes | Sparse, TF-IDF, NLP |
from sklearn.preprocessing import MaxAbsScaler
X_sparse = TfidfVectorizer(max_features=5000).fit_transform(df['review_text'])
X_scaled = MaxAbsScaler().fit_transform(X_sparse) # divides by |max|, keeps sparsity & zeros
Which Algorithms Need Scaling?
| Algorithm | Verdict | Why |
|---|---|---|
| KNN · K-Means | ⚠ scale | Euclidean distance — big ranges dominate |
| SVM | ⚠ scale | Margin is set by the largest-scale feature |
| Neural networks | ⚠ scale | Gradients ∝ input magnitude → explode / vanish |
| Linear · Logistic · Ridge · Lasso | ⚠ scale | Coefficients & penalties depend on scale |
| PCA | ⚠ scale | Chases variance — hijacked by the biggest feature |
| Decision Tree · RF · XGBoost | ✓ skip | Split on thresholds — scale-invariant |
| Naïve Bayes | ~ optional | Per-feature variance absorbs scale |
A split like "salary > 500,000" gives the same partition whether salary is in rupees or scaled to 0–1. Scaling a tree model wastes effort and can make features harder to interpret.
The Payoff — Accuracy Before vs After
Distance and gradient models leap when features share a scale; the tree ensemble is flat because it never used magnitude in the first place. The chart is the scale/skip rule, measured.
The Data-Leakage Trap
# ✅ BEST — a Pipeline is leak-proof by design
pipe = Pipeline([('scaler', StandardScaler()), ('model', LogisticRegression())])
pipe.fit(X_train, y_train); pipe.score(X_test, y_test) # scaler fits on train inside fit()
Choosing the Right Scaler
Sparse? MaxAbs. Legit outliers? Robust. Neural net / images? MinMax. Otherwise? StandardScaler — the safe default for the majority of models.
Same Features, Four Scalers
A Multi-Scaler Production Pipeline
std = Pipeline([('imp', SimpleImputer('median')),
('sc', StandardScaler())])
rob = Pipeline([('imp', SimpleImputer('median')),
('sc', RobustScaler())])
mm = Pipeline([('imp', SimpleImputer('median')),
('sc', MinMaxScaler())])
cat = Pipeline([('imp', SimpleImputer('most_frequent')),
('oh', OneHotEncoder(
handle_unknown='ignore'))])
pre = ColumnTransformer([
('std', std, normal_num),
('rob', rob, skewed_num),
('mm', mm, bounded),
('cat', cat, cat_cols)])
pipe = Pipeline([
('prep', pre),
('model', GradientBoostingClassifier())])
pipe.fit(X_train, y_train)
joblib.dump(pipe, 'production.pkl')
Common Pitfalls & Their Fixes
| Symptom | The trap | The fix |
|---|---|---|
| One feature dominates KNN/SVM | No scaling at all | StandardScaler / MinMaxScaler |
| Inflated validation score | Fit scaler on full data | Fit on X_train only — use a Pipeline |
| Normals crushed near zero | MinMax with an outlier | RobustScaler |
| Skew survives scaling | Scaling a skewed column | log1p / power transform first |
| Sparse matrix blows up in RAM | StandardScaler centres zeros | MaxAbsScaler (keeps sparsity) |
| Wasted effort, worse interpretability | Scaling a tree model | Skip — trees are scale-invariant |
| Model fails at inference | Saved model, not the scaler | joblib.dump(pipeline) |
The Golden Rules — 1 to 4
The Golden Rules — 5 to 8 & Takeaway
|skew| > 1, log/power-transform first — scaling never fixes shape.describe() — Standard should give mean≈0, std≈1; MinMax should give min 0, max 1.When the triage model ignored creatinine because blood pressure had bigger numbers, it wasn't a data or model problem — it was a scale problem. Four lines of code moved F1 on critical patients from 0.54 to 0.81.
📐 End of tutorial · Press ← to review, or click Restart