Data Preprocessing
Raw data is almost never ready for a learning algorithm. Features live on different scales, categories are stored as strings, values are missing, and distributions are skewed. Preprocessing transforms raw features into a representation the algorithm can exploit β and done wrong, it is also the easiest place to leak information (see Validation & Data Leakage).
Why scale features?
Many algorithms are distance-based or gradient-based, and both are distorted when features have wildly different magnitudes:
- k-NN computes Euclidean distances: a feature ranging in the thousands (income) drowns out one ranging in the tens (age);
- Gradient descent converges slowly on elongated loss surfaces created by unscaled features;
- SVMs and regularized models (Ridge/Lasso) penalize coefficients as if features were comparable;
- PCA finds directions of maximum variance β the largest-scale feature wins by default.
Tree-based models (decision trees, random forests, gradient boosting) are the notable exception: they split on thresholds, so monotonic transformations do not affect them.
Scaling methods
Standardization (z-score): center at zero, unit variance β
Min-max normalization: squeeze into \([0, 1]\) β
Robust scaling: use median and IQR instead of mean and standard deviation β
The choice matters when outliers are present. Min-max is fully determined by the two most extreme points; standardization is somewhat distorted by them; robust scaling ignores them:
| Scaler | Formula anchors | Sensitive to outliers? | Typical use |
|---|---|---|---|
StandardScaler | mean, std | moderately | default for linear models, SVM, PCA |
MinMaxScaler | min, max | highly | when a bounded range is required (e.g. pixel values, some neural nets) |
RobustScaler | median, IQR | robust | data with heavy tails / outliers |
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # learn ΞΌ, Ο on TRAIN only
X_test_scaled = scaler.transform(X_test) # apply the SAME ΞΌ, Ο
Fit on train, transform on test
fit learns the parameters (\(\mu, \sigma\), min/max...). Calling fit (or fit_transform) on the test set β or on the full dataset before splitting β leaks information about the test distribution into training. This is the single most common leakage bug, and Pipelines exist largely to prevent it.
Skewed features
Right-skewed features (income, prices, counts) often benefit from a log transform, \(x' = \log(1 + x)\), which compresses the long tail and makes the distribution more symmetric. More general tools: PowerTransformer (Box-Cox, Yeo-Johnson) and QuantileTransformer.
Encoding categorical features
Algorithms consume numbers, not strings. The two workhorses:
One-hot encoding β one binary column per category:
from sklearn.preprocessing import OneHotEncoder
enc = OneHotEncoder(handle_unknown='ignore') # unseen categories β all zeros
- Safe for nominal categories (no order): city, color, product type;
- Explodes dimensionality for high-cardinality features (zip codes β thousands of columns).
Ordinal encoding β map categories to integers:
from sklearn.preprocessing import OrdinalEncoder
enc = OrdinalEncoder(categories=[['small', 'medium', 'large']])
- Correct for ordinal categories (small < medium < large);
- Wrong for nominal ones: encoding cities as SΓ£o Paulo=0, Rio=1, Recife=2 invents an order and distances that do not exist β linear models and k-NN will happily exploit the fiction.
For high-cardinality features, consider target encoding (replace each category by a smoothed mean of the target) β powerful but leakage-prone: it must be fit inside cross-validation.
Missing value imputation
Building on the EDA discussion of missingness mechanisms:
from sklearn.impute import SimpleImputer, KNNImputer
SimpleImputer(strategy='median') # numeric: robust default
SimpleImputer(strategy='most_frequent') # categorical
KNNImputer(n_neighbors=5) # impute from similar rows
Two useful practices:
- add a missing-indicator column (
add_indicator=True) β the fact that a value was missing is often predictive; - impute inside a Pipeline, so the imputation statistics are learned from training folds only.
Never impute the target
Rows with a missing target should be dropped from supervised training β inventing labels manufactures signal that does not exist.
The preprocessing map
flowchart TD
A[Raw feature] --> B{Type?}
B -->|numeric| C{Skewed?}
C -->|yes| D[log / power transform] --> E
C -->|no| E{Outliers?}
E -->|yes| F[RobustScaler]
E -->|no| G[StandardScaler]
B -->|categorical| H{Ordered?}
H -->|yes| I[OrdinalEncoder]
H -->|no| J{Cardinality?}
J -->|low| K[OneHotEncoder]
J -->|high| L[target encoding / grouping] Class materials
Class notebook (in Portuguese)
Hands-on notebook used in class β Aula 03 β NormalizaΓ§Γ£o: open in Colab