Machine Learning · Insper

Data Preprocessing

Fundamental approach

How raw columns become numbers a model can use without being fooled

The algorithm only sees numbers

It minimizes the cost with the numbers you hand it. If they are badly prepared, it minimizes the wrong thing, with full confidence.

incompatible scales

the column with the biggest numbers dominates distances and penalties

Fare 0 to 512, SibSp 0 to 8

text

there is no arithmetic with "female"

Sex, Embarked, Cabin, Ticket, Name

holes

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.

Every step learns something (a mean, a median, a list of categories), and it must learn it from the training rows only.

The path of this part

  1. Scaleunits decide distances; four scalers
  2. Fit on train onlyfit once on the training rows, transform the rest
  3. Fix the shapeskewness; a log, not a scaler
  4. Encode categoriesordinal, one-hot, unseen categories
  5. Impute the holeswhy it is missing; one value; a flag
Each stage answers one question: which column owns the distance? → where may a step learn its numbers? → what can a scaler not fix? → how does a category become a number? → what goes in the holes?
Stage 1 · which column owns the distance?

Three passengers, by hand

passengeragefare
ABraund, Mr. Owen Harris (3rd class)22.0£7.25
BConnors, Mr. Patrick70.5£7.75
CPears, Mrs. Thomas (1st class)22.0£66.60
Predict. Using the Euclidean distance on (age, fare), which passenger is closest to A: the 70-year-old B, or the 22-year-old C?

In raw units, the 70-year-old wins

pairage differencefare differencedistance
A–B48.5 years£0.50\(\sqrt{48.5^2 + 0.5^2} = 48.50\)
A–C0 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.

Predict. Now measure each column in standard deviations, \(z = (x - \mu)/\sigma\), with \(\mu\) the column mean and \(\sigma\) its standard deviation: \(\sigma_{\text{Age}} = 14.52\), \(\sigma_{\text{Fare}} = 52.88\). Does the answer change?

Standardized, the ranking flips

Raw units: B is nearest to A; standardized: C is nearest

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.

Neither answer is "true": the raw one is an accident of the units; the standardized one is a choice that gives each column the same say.

Simulator: the unit decides the neighbors

Predict. Slide the income unit from thousands to cents. Do the 5 nearest neighbors of the red point stay the same? Then press standardize.

Which column owns the distance?

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:

columnrawstandardized
Fare92.9%
25%
Age7.0%
25%
SibSp0.0%25%
Parch0.0%25%
A k-NN (k nearest neighbors) on raw columns is, in practice, a k-NN on fare. Standardized, every column has variance 1 and each gets 25%.

Which models care about scale

care

  • distance-based: k-NN, k-means, kernel SVMs (support vector machines)
  • variance-based: PCA, principal component analysis (the largest-scale feature wins by default)
  • gradient-based and penalized: linear and logistic regression, neural networks, Ridge/Lasso

do not care

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.

Four scalers: \(x' = (x - \text{center}) / \text{spread}\)

scalerformulacenterspreadresult
StandardScaler\((x - \mu) / \sigma\)meanstandard deviationmean 0, std 1
MinMaxScaler\((x - x_{\min}) / (x_{\max} - x_{\min})\)minimumrangevalues in \([0, 1]\)
RobustScaler\((x - \text{median}) / \text{IQR}\)medianIQR \(= 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]\)
A scaler is a linear map applied to each column separately. The four differ only in which statistics they use. IQR: interquartile range, between the first quartile \(Q_1\) and the third \(Q_3\).

Four scalers, step by step

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?

Same geometry, different weight next to other columns

On a common axis, the four ordinary incomes span 0.2, 0.077, 0.075 and 1.5; the robust outlier lands at 18.5

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.

Under MinMaxScaler, next to an age column with std 1, two ordinary households look almost identical in income, and age decides everything.

Choosing a scaler

scalereffect of outlierstypical use
StandardScalermoderate: they inflate \(\sigma\)the default: linear models, SVM, PCA, neural networks
MinMaxScalerstrong: one extreme value compresses the resta bounded range is required (pixel intensities)
RobustScalerweak: they are simply left far awaycolumns with outliers you decided to keep
MaxAbsScalerstrong, like min-maxsparse data: zeros stay zeros
  • Normalization is ambiguous (min-max? "make it normal"? scikit-learn's Normalizer rescales each row). Write the formula.
  • A new value outside the training range leaves the interval: an income of 80 becomes \((80-1)/39 = 2.03\) under min-max.
Stage 2 · where may a step learn its numbers?

Fit on the training set, transform everything else

A mean or a standard deviation is a parameter of your model, just like a weight.

fit

learns the statistics and stores them (mean_, scale_)

transform

applies the stored statistics, frozen

fit_transform

both, 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.

Four ways to call the scaler, step by step

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?

Stage 3 · what can a scaler not fix?

Skewed features: a scaler cannot change the shape

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.

Predict. The raw fare has skewness 4.79. What is it after StandardScaler?
fareskewness
raw4.79
standardized4.79
square root2.09
\(\log(1 + x)\), np.log1p0.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.

Stage 4 · how does a category become a number?

Encoding: three kinds of categories

nominal

no order: port, color, city

→ one-hot

ordinal

a real order: ticket class, education, size S < M < L

→ integers in that order

binary

two categories: Sex

→ one 0/1 column

The tempting shortcut: OrdinalEncoder numbers the ports alphabetically, Cherbourg = 0, Queenstown = 1, Southampton = 2.

Codes or one-hot, row by row

The three ports, encoded twice. Predict. With integer codes, which two ports does a distance put farthest apart? And with one-hot?

One slope cannot fit two steps

survival by port

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.

what to do

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.

Ordinal: you declare the order


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.

One-hot: a category never seen in training

Trained on the ports S, C, S, Q, S, C. Two new passengers arrive: one from Q, one from Belfast (B).

new portEmbarked_CEmbarked_QEmbarked_Sdefault
Q010fine
B000ValueError: the prediction crashes
With 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.
Stage 5 · what goes in the holes?

Missing values: why is it missing?

mechanismwhat it meansTitanic example
MCAR, missing completely at randomdepends on nothingthe 2 missing Embarked, plausibly
MAR, missing at randomdepends on other observed columnsAge, missing more in 3rd class
MNAR, missing not at randomdepends on the missing value itselfCabin: no cabin of their own

Age missing, by class

1st 13.9% · 2nd 6.0% · 3rd 27.7%

survival

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.

Mean imputation, step by step

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?

Keep the fact that it was missing


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'.

Never impute the target. Rows with a missing label leave supervised training; inventing labels manufactures signal.

Summary

  1. Raw units decide distances by accident: scale what distances, variances and penalties use.
  2. Scalers differ in center and spread; an outlier sets the scale of all but RobustScaler.
  3. Fit on train, transform everything else.
  4. A scaler cannot fix the shape: log1p, then scale.
  5. One-hot for nominal, declared order for ordinal; handle_unknown='ignore'.
  6. Ask why it is missing; filling shrinks the spread; keep a flag.
symptomwhat to do
one column decides every neighborstandardize the numeric columns
scaled test columns with mean exactly 0the scaler saw the test: fit on train only
Fundamental lab · about 30 min, in pairs

Preparing the Titanic, column by column: steps 1 to 6

  • Inventory, split first (712 train, 179 test), why Age is missing, impute it, one-hot Sex, Embarked, Pclass, scale the numbers.
  • Lab notebook: open it in Jupyter or Colab (File → Upload notebook).
  • At every step, answer the Predict before running.
  • Simulations: the shrinkage law, on holes you make (step 4) and whose neighbors? (step 6).

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.