Scaling
Which models care about units, which do not, and why "always scale" is cargo cult
StandardScalerRobustScalerMinMaxScalerfit_transformmean_scale_Watch it happen
Play it through, or step back and forth yourself.
distance_km runs 0.4 to 6.4. temp runs 14.6 to 45.4. Nothing is wrong with that — they measure different things — but some models take the numbers at face value and conclude that temperature matters seven times more.
The idea
distance_km runs from 0.4 to 6.4. temp runs from 14.6 to 45.4. Nothing is wrong with that — they measure different things — but a model that computes distances between rows adds those up as if they were the same kind of number, and concludes that temperature matters seven times more than how far the rider has to go.
StandardScaler
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler().fit(X_train)
scaler.mean_ # [2.88 3.53 14.59 29.46 8.82]
scaler.scale_ # [1.15 1.68 4.08 5.19 2.76]
X_train_s = scaler.transform(X_train)(x − mean) / std, per column. Every column arrives centred at 0 with a standard deviation of 1. Note that both learned attributes carry the trailing underscore from lesson 2 — they came from the data.
Does it actually matter?
Here's the honest answer, measured on our data with 5-fold cross-validated AUC:
model unscaled scaled Δ
kNN (k=15) 0.7538 0.7854 +0.032
logistic regression 0.8143 0.8142 −0.000
decision tree (d=3) 0.7853 0.7853 0.000For kNN it is worth 0.032 AUC. For the other two it is worth nothing. "Always scale" is advice that happens to be safe rather than advice that's correct, and knowing which case you're in is the actual skill.
Who needs it
Three families, for three different reasons:
- Distance-based — kNN, k-means, SVM with an RBF kernel. The distance between two rows is the model, and an unscaled column dominates that sum.
- Gradient-based — neural networks,
SGDClassifier. Wildly different scales make the loss surface a narrow valley, and optimisers crawl along it. - Penalised — ridge, lasso, logistic regression with a strong penalty. The penalty applies to the raw coefficients, so a column measured in metres is penalised a thousand times more than the same column in kilometres. Lesson 15.
There's a fourth reason that applies even when the score doesn't move: interpretability. Unscaled coefficients are in units per unit, so you can't compare them; scaled ones are per standard deviation, so you can. That's why our lesson-4 model was scaled even though it made no difference to accuracy.
Who doesn't
Trees, and everything built from them — random forests, gradient boosting. A tree asks "is distance_km > 3.2?", one column at a time:
is distance_km > 3.2 ?
is (distance_km − 2.88) / 1.15 > 0.28 ?Identical split, identical tree. Any monotonic rescaling is invisible to a tree, because it only ever compares a column to a threshold within that same column. Scaling a forest is harmless and pointless.
Which scaler
StandardScaler— the default. Assumes no wild outliers, because the mean and standard deviation are both sensitive to them.RobustScaler— median and IQR instead. Reach for it when a column has extreme values you don't want to remove.MinMaxScaler— squashes to 0–1. Useful when you need a bounded range, but a single extreme value compresses everything else into a corner.PowerTransformer— for columns that are badly skewed rather than badly scaled. Different problem, different tool.
And a trap: Normalizer scales each row to unit length, not each column. It sounds like the others and does something entirely different. It's for text vectors, not for tabular features.
The rule that actually matters
X_train_s = scaler.fit_transform(X_train) # fit here, and ONLY here
X_test_s = scaler.transform(X_test) # apply, never fitFit the scaler on everything and the test set's mean is inside the numbers the model trained on. It's a small leak, it inflates your score consistently, and nothing will catch it.
It gets considerably worse inside cross-validation, where the test fold changes every round — scale once up front and you've leaked into all five folds. Lesson 13 makes this structurally impossible.
See it run
The lesson's code, ready to run and to fiddle with.
Putting the kettle on…
Starting up…
Worked example
not gradedAlready written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.
tryswapping StandardScaler for RobustScaler and seeing whether kNN cares.
Your turn
3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.
Fit a StandardScaler on the median-imputed numeric columns and return [mean_, scale_], each rounded to 2 decimal places, as lists of floats.
Show that scaling matters for kNN. Return the 5-fold AUC for KNeighborsClassifier(15) with and without a StandardScaler, as a list rounded to 4 places.
Now show that a tree doesn't care. Same comparison with DecisionTreeClassifier(max_depth=3, random_state=0). The two numbers should be identical.
