Categorical Encoding
Turn categories into numbers without lying
Categorical features become numbers via one-hot, ordinal, frequency, target and embedding encodings; the right choice balances dimensionality, ordinality and leakage risk.
What you will learn
- Choose between nominal and ordinal encodings from the data semantics
- Fit one-hot, ordinal, frequency and target encoders and know when each is appropriate
- Implement target encoding safely inside cross-validation to avoid leakage
What is this?
Categorical encoding maps non-numeric values to numbers. One-hot/dummy creates indicator columns (nominal); OrdinalEncoder assigns ranks (ordered); Frequency/Count encoding replaces labels with their counts; Target/Mean encoding replaces labels with the mean target per category (often smoothed). Choice trades off dimensionality, information, cardinality and leakage risk.
Why do you need it?
- Models compute on numbers, not strings — encoding is mandatory, not optional.
- One-hot on high-cardinality columns explodes features; smart encodings keep matrices lean.
- Ordinality is real (size S<M<L); encoding order mistakes bake fake distances into the model.
- Target encoding packs signal per column but leaks unless done fold-wise.
Mental model
Translating words between languages
Encoding categories to numbers
A naive translator picks the first dictionary match and loses tone; so can a naive encoder. Ordinality, rarity and meaning all matter — a 1:1 glossary is not a translation.
How it works
- Inspect cardinality and semantics: nominal vs ordinal vs hierarchical.
- Ordinal: use OrdinalEncoder only for true orders; map explicitly, don't guess alphabetical order.
- Nominal low-cardinality: one-hot via pd.get_dummies or OneHotEncoder(handle_unknown='ignore').
- Nominal high-cardinality: frequency or target encoding, or embeddings for text-like codes.
- Fit on train folds only; reuse the same mapping in production.
Encoding decision flow.
Examples
One-hot vs frequency vs safe target encoding
import pandas as pdimport numpy as npfrom sklearn.model_selection import KFold df = pd.DataFrame({ "city": ["nyc", "sf", "nyc", "la", "nyc", "sf", "la", "nyc"], "amount": [100, 40, 80, 20, 60, 55, 25, 90],}) onehot = pd.get_dummies(df["city"], prefix="city") freq = df["city"].value_counts(normalize=True)df["city_freq"] = df["city"].map(freq) smooth = 5.0glob = df["amount"].mean()df["city_target"] = 0.0for train_idx, val_idx in KFold(5, shuffle=True, random_state=1).split(df): tr = df.iloc[train_idx] va = df.iloc[val_idx] agg = tr.groupby("city")["amount"].agg(["mean", "count"]) m = (agg["count"] * agg["mean"] + smooth * glob) / (agg["count"] + smooth) df.loc[va.index, "city_target"] = va["city"].map(m) df["city_target_glob"] = df["city"].map(lambda c: (df.loc[df["city"]==c, "amount"].mean())) print(df)print(onehot.head())One-hot stays safe for low cardinality; frequency encoding shrinks the matrix; and target encoding uses only out-of-fold means — the commented last line shows the leaking shortcut to never copy.
In production
- Retail: store IDs (thousands of categories) → frequency + demand-target encodings beat 10k one-hot columns.
- Insurance: code-level policy types → ordinal embedding captures hierarchy (auto -> collision -> liability).
- NLP-lite: merchant names → target encoding with smoothing; one-hot would drown the model.
Best practices
- Reserve adjacency for true order; always pass handle_unknown so unseen production categories don't crash; verify encoding columns count in train == prod; smooth target encoding with a global prior; nest every target-derived computation inside the CV loop.
Common mistakes
- Ordinal-encoding nominal categories with guessed order; one-hotting everything and blowing up memory; target-encoding on the full dataset (massive leakage); forgetting unseen categories in prod; relying on alphabetical orders from get_dummies.
Debugging tips
- Diff the set of categories in train vs prod before scoring.
- After one-hot, check pd.get_dummies column names match between train and live rows.
- For target encoding, verify validation receives no train-category mean from its own rows.
Interview checkpoint
Quick quiz
One-hot encoding a 500-category column produces how many columns?
Why should target encoding be fit inside CV folds?