How raw columns become numbers a model can use without being fooled
It minimizes the cost with the numbers you hand it. If they are badly prepared, it minimizes the wrong thing, with full confidence.
the column with the biggest numbers dominates distances and penalties
Fare 0 to 512, SibSp 0 to 8
there is no arithmetic with "female"
Sex, Embarked, Cabin, Ticket, Name
most implementations refuse NaN (not a number, a missing value)
Age missing for 19.9%, Cabin for 77.1%
Each step is measured on a few numbers you can check with a calculator, then on the Titanic (891 passengers of the 1912 voyage). EDA (exploratory data analysis) looked at the table; this lesson changes it.
| passenger | age | fare | |
|---|---|---|---|
| A | Braund, Mr. Owen Harris (3rd class) | 22.0 | £7.25 |
| B | Connors, Mr. Patrick | 70.5 | £7.75 |
| C | Pears, Mrs. Thomas (1st class) | 22.0 | £66.60 |
| pair | age difference | fare difference | distance |
|---|---|---|---|
| A–B | 48.5 years | £0.50 | \(\sqrt{48.5^2 + 0.5^2} = 48.50\) |
| A–C | 0 years | £59.35 | \(\sqrt{0^2 + 59.35^2} = 59.35\) |
48.5 years is a smaller number than 59.35 pounds. Nothing says a year and a pound should weigh the same: it is just what happens when you subtract raw numbers.
A–B: \(48.5 / 14.52 = \mathbf{3.34}\) std
A–C: \(59.35 / 52.88 = \mathbf{1.12}\) std
\(\mu\) and \(\sigma\) computed over the 714 passengers whose age is known.
Predict. Slide the income unit from thousands to cents. Do the 5 nearest neighbors of the red point stay the same? Then press standardize.
For two random rows, the expected squared difference in a column is twice its variance: each column's share of the distance is its share of the total variance. Titanic, four numeric columns:
| column | raw | standardized | |
|---|---|---|---|
Fare | 92.9% | 25% | |
Age | 7.0% | 25% | |
SibSp | 0.0% | 25% | |
Parch | 0.0% | 25% |
Trees, random forests, gradient boosting: they split on one feature at a time ("is Age < 12?").
Any increasing transformation moves the threshold but sends the same rows to each side.
| scaler | formula | center | spread | result |
|---|---|---|---|---|
StandardScaler | \((x - \mu) / \sigma\) | mean | standard deviation | mean 0, std 1 |
MinMaxScaler | \((x - x_{\min}) / (x_{\max} - x_{\min})\) | minimum | range | values in \([0, 1]\) |
RobustScaler | \((x - \text{median}) / \text{IQR}\) | median | IQR \(= Q_3 - Q_1\) | median 0, IQR 1 |
MaxAbsScaler | \(x / \max \lvert x \rvert\) | none (0 stays 0) | largest \(\lvert x \rvert\) | values in \([-1, 1]\) |
Five household incomes, R$ thousand: 1, 2, 3, 4 and 40. Predict. Which scaler will keep the four ordinary households most spread out, and which will squeeze them together?
Inside the column the ordinary households always occupy the same 8% of the range: a linear map cannot change proportions. What changes is how big their differences are compared with the other columns, and that is what a distance adds up.
MinMaxScaler, next to an age column with std 1, two ordinary households look almost identical in income, and age decides everything.| scaler | effect of outliers | typical use |
|---|---|---|
StandardScaler | moderate: they inflate \(\sigma\) | the default: linear models, SVM, PCA, neural networks |
MinMaxScaler | strong: one extreme value compresses the rest | a bounded range is required (pixel intensities) |
RobustScaler | weak: they are simply left far away | columns with outliers you decided to keep |
MaxAbsScaler | strong, like min-max | sparse data: zeros stay zeros |
Normalizer rescales each row). Write the formula.A mean or a standard deviation is a parameter of your model, just like a weight.
fitlearns the statistics and stores them (mean_, scale_)
transformapplies the stored statistics, frozen
fit_transformboth, on the same data
fit once, on the training rows. Validation, test and production only ever see transform. The same rule holds for every step of this lesson: scalers, encoders, imputers.Training ages 20, 25, 30, 35, 40; new passengers aged 60 and 70. Predict. If someone calls StandardScaler().fit_transform(test) on the two new rows, what values do 60 and 70 receive?
Titanic fares: half cost less than £14.46, three cost £512, and 15 tickets were free. Skewness \(= \frac{1}{n}\sum z_i^3\), the mean of the cubed z-scores of the \(n\) values: 0 when symmetric, positive with a long right tail.
StandardScaler?| fare | skewness |
|---|---|
| raw | 4.79 |
| standardized | 4.79 |
| square root | 2.09 |
\(\log(1 + x)\), np.log1p | 0.39 |
A nonlinear map is needed. Log turns multiplications into additions (£8 → £80 and £50 → £500 become equal steps); log1p because \(\log 0 = -\infty\). Order: shape, then scale; check the skewness afterwards.
no order: port, color, city
→ one-hot
a real order: ticket class, education, size S < M < L
→ integers in that order
two categories: Sex
→ one 0/1 column
The tempting shortcut: OrdinalEncoder numbers the ports alphabetically, Cherbourg = 0, Queenstown = 1, Southampton = 2.
The three ports, encoded twice. Predict. With integer codes, which two ports does a distance put farthest apart? And with one-hot?
| C (code 0) | 0.554 |
| Q (code 1) | 0.390 |
| S (code 2) | 0.337 |
Steps of 16.4 and 5.3 points. A linear model gives the code column one coefficient, so both steps are forced to be equal.
One-hot gives each port its own coefficient, and no alphabet decides the order.
Trees are the exception: cuts at 0.5 and 1.5 isolate each code, so integer codes are fine for them.
OrdinalEncoder().fit(levels).categories_
# ['bachelor', 'high school', 'master'] alphabetical!
OrdinalEncoder(categories=[['high school', 'bachelor', 'master']])
# codes now follow the real order: 0, 1, 2
The alphabet puts high school between bachelor and master.
Pclass is already 1, 2, 3. Ordinal (one column) or nominal (three one-hot columns) is a modeling choice: ordinal assumes 1st → 2nd matters as much as 2nd → 3rd.Trained on the ports S, C, S, Q, S, C. Two new passengers arrive: one from Q, one from Belfast (B).
| new port | Embarked_C | Embarked_Q | Embarked_S | default |
|---|---|---|---|---|
| Q | 0 | 1 | 0 | fine |
| B | 0 | 0 | 0 | ValueError: the prediction crashes |
handle_unknown='ignore', B becomes 0 0 0, "none of the categories I know". Common in practice: a rare category may simply not land in the training split.| mechanism | what it means | Titanic example |
|---|---|---|
| MCAR, missing completely at random | depends on nothing | the 2 missing Embarked, plausibly |
| MAR, missing at random | depends on other observed columns | Age, missing more in 3rd class |
| MNAR, missing not at random | depends on the missing value itself | Cabin: no cabin of their own |
1st 13.9% · 2nd 6.0% · 3rd 27.7%
age known 40.6% · age missing 29.4%
dropna() would cost 177 rows (19.9%), mostly 3rd class: the passengers who died most would be under-represented.Six values, two missing: 2, 4, ?, 6, ?, 8. Predict. After both holes are filled with the mean, will the variance go up, stay at 5 or go down?
imp = SimpleImputer(strategy='median', add_indicator=True)
imp.get_feature_names_out() # ['Age' 'missingindicator_Age']
# passenger 1: [22, 0] passenger 6: [28, 1]
Missing age goes with lower survival (29.4% against 40.6%). The flag costs one column and lets the model decide whether missingness is informative. For categories: a category of its own, 'unknown'.
RobustScaler.log1p, then scale.handle_unknown='ignore'.| symptom | what to do |
|---|---|
| one column decides every neighbor | standardize the numeric columns |
| scaled test columns with mean exactly 0 | the scaler saw the test: fit on train only |
Age is missing, impute it, one-hot Sex, Embarked, Pclass, scale the numbers.Pace: steps 1 to 3 in 8 min; step 4 and the first simulation in 10 min; steps 5 and 6 and the second in 12 min.