Widths are checkable by hand on the lesson’s table. Five training rows, two numeric columns and two categorical columns:
age [30, 40, 45, 50, 60] median 45
μ 45 σ 10
income present [30000, 50000, 70000, 90000]
median = (50000 + 70000) / 2 = 60000
after imputing 60000:
values [50000, 60000, 90000, 30000, 70000]
μ = 300000 / 5 = 60000
deviations [−10000, 0, 30000, −30000, 10000]
σ = sqrt(2,000,000,000 / 5) = 20000
one-hot widths
city [chicago, houston, la, new_york] → 4
plan [basic, free, premium] → 3
X = 2 numeric + 4 city + 3 plan → 9
drop="first": 2 + 3 + 2 → 7
serve row age 58 · income 96000 · city "seattle" · plan "premium"
age z = (58 − 45) / 10 = 1.30
income z = (96000 − 60000) / 20000 = 1.80
city "seattle" unseen → [0, 0, 0, 0]
plan "premium" → [0, 0, 1]
X = [1.30, 1.80, 0, 0, 0, 0, 0, 0, 1]
The two per-row checks are the whole discipline in miniature: the imputed 60000 makes the mean exactly 60000 and the std exactly 20000, and the unseen city contributes no signal instead of an exception. The imputation value and the μ/σ are all fitted on the five training rows — the serve row is transformed, never fitted.
Adding a polynomial branch. Suppose the two numeric columns deserve interaction terms. PolynomialFeatures(degree=2, include_bias=False) turns [age, income] into [age, income, age², age·income, income²] — 2 → 5 columns. It belongs inside the numeric pipeline, after imputation and before scaling: the row (45, 60000) produces 2025, 2.7 million and 3.6 billion, and those three magnitudes must be standardized like any other feature. The output grows from 9 to 5 + 4 + 3 = 12 columns, and every new column is still fitted and transformed inside the same train-only pipeline.