Almost every real dataset arrives with columns that are words, not numbers:
a colour is "red," a city is "Osaka," a payment method is "credit card." A model built on
That last clause is the whole art. The wrong encoding quietly injects a false claim — that "Tuesday" is bigger than "Monday," or that "Tokyo" sits halfway between "Sydney" and "Cairo" — and the model dutifully learns the fiction. This page is a tour of the three encodings you reach for most, and the trap hiding inside each.
The safest default for an unordered (nominal) category is one-hot encoding: give every distinct value its own column, and mark a 1 in the column that matches, 0 everywhere else. A single "colour" column becomes a little block of 0/1 columns:
| colour | colour_red | colour_green | colour_blue |
|---|---|---|---|
| red | 1 | 0 | 0 |
| green | 0 | 1 | 0 |
| blue | 0 | 0 | 1 |
| red | 1 | 0 | 0 |
Now each colour is a corner of a coordinate space, all equally far apart — no fake ordering, no fake
distances. This is exactly what you want when the categories have no natural rank. The cost is
width:
Tree models and regularised models don't strictly need the drop, but for plain linear/logistic
regression it matters: keep all
Some categories do have a rank: small < medium < large,
bronze < silver < gold, a survey's strongly-disagree … strongly-agree. For
these, ordinal encoding maps them to ordered integers
(
The danger is using it when there is no order. "Label encoding" — slapping arbitrary integers on unordered categories (red→0, green→1, blue→2) — looks tidy and is a classic beginner mistake: it tells a linear or distance-based model that green is between red and blue, and that blue is "twice" green. Trees can sometimes cope (they only split on thresholds), but for anything geometric this manufactures relationships that don't exist. Ordinal encoding is for ordered categories only.
When a category has hundreds or thousands of levels — zip codes, product IDs, user agents — one-hot encoding explodes into a sea of columns and label encoding lies. A sharper tool is target encoding (also called mean encoding): replace each category with the average value of the target for the rows in that category. If 8% of orders from postcode SW1 were fraudulent, encode SW1 as 0.08. One dense, informative column, whatever the cardinality.
Target encoding is built from the target you are trying to predict — so if you compute the
per-category means over the whole training set and then let a row use the mean it helped create,
you have handed the model a peek at its own label. Validation scores look brilliant and collapse in
production. This is textbook
A useful smoothing formula blends the category mean
where
The three tools line up neatly against how many levels a column has:
| Situation | Reach for | Because |
|---|---|---|
| Few, unordered levels | One-hot (drop one) | No fake order; width is affordable |
| Ordered levels | Ordinal | The order is genuine signal |
| Many levels (high cardinality) | Target / mean encoding | Stays one column; but fit inside CV folds |
High cardinality is the recurring villain: a "city" column with 5,000 values becomes 5,000 one-hot columns, most of them almost always zero — a sparse, memory-hungry mess that also invites overfitting because rare cities appear only a handful of times. Target encoding, frequency encoding (replace a category by how often it occurs), or grouping rare levels into an "other" bucket are the usual escapes.
With k mutually exclusive categories, knowing that a row is "not red" and "not green" forces it to be blue — the last dummy is fully determined by the others. That redundancy is precisely the collinearity that breaks a linear solver, and precisely why removing one column costs nothing: the reference category is encoded as "all zeros." The kept coefficients simply become contrasts against that baseline, which is often more interpretable, not less.
Kaggle Learn's Feature Engineering course walks through one-hot, ordinal and target encoding on real competition data in Python — including how to build target encoding safely so it doesn't leak. Practise the tradeoffs there on a dataset with genuinely high-cardinality columns.