One-Hot Encoding: Why It Breaks Between Train and Test

Your model works perfectly in testing. You deploy it. It crashes on the first real prediction.

ValueError: The feature names should match those that were passed during fit.
Feature names seen at fit time, yet now missing:
- Delhi

If you've seen an error like this — a feature that's missing, or a mismatch in feature names — it almost always traces back to one decision: how a categorical column got converted into numbers.

What Actually Happened

Somewhere in the pipeline, a column like city — Bangalore, Mumbai, Delhi — got converted into numbers. That conversion is called one-hot encoding. Instead of one column holding text labels, you get one new column per category, each holding a 1 or a 0.

Bangalore becomes city_Bangalore = 1, everything else 0. Mumbai becomes city_Mumbai = 1, everything else 0.

Why Not Just Use 1, 2, 3?

Models only understand numbers. They can't do math on the word "Bangalore." But if you just labeled cities 1, 2, 3, the model would assume Delhi is "greater than" Bangalore — a ranking that doesn't exist. One-hot encoding is how you hand a model categorical data without inventing a fake order.

The One Line That Does It

from sklearn.preprocessing import OneHotEncoder

encoder = OneHotEncoder()
X_train_encoded = encoder.fit_transform(X_train[['city']])

fit_transform does two things at once: it learns every category it sees in the training data, and converts them right there. That's the whole operation.

Where It Actually Breaks

A very common shortcut is pandas' get_dummies instead of scikit-learn's encoder:

pd.get_dummies(train_df['city'])   # → 3 columns
pd.get_dummies(test_df['city'])    # → 2 columns

Call it separately on training and test data, and you can get a different number of columns on each side. Training data had Bangalore, Mumbai, and Delhi — three new columns. Test data only had Bangalore and Mumbai that day — two columns. The model expects three inputs and gets two. Crash. That's the exact error from the start of this post — the real one, not a mockup.

The fix: fit the encoder once, on training data only, and reuse that same encoder — never regenerate columns independently on new data.

And to be precise about what's actually broken here: it's not that pandas is bad and scikit-learn is good. Call fit_transform() a second time on the test set instead of transform(), and scikit-learn breaks the exact same way — fit_transform forgets what it learned and relearns from whatever it's given. The rule isn't "use this library." It's "fit once, transform everywhere, never fit twice."

One More Thing to Watch For

This only works cleanly when there aren't too many categories. A city column with 5 values is fine. A user_id column with 50,000 unique values turns into 50,000 new columns — a dataset that's enormous and mostly zeros. That's usually a sign a different technique is needed entirely.

Get the Code

The full breakdown, including the production crash reproduction: OneHotEncoding.ipynb.

What's Next

One-hot encoding solves the fake-ranking problem, but it introduces its own scaling problem the moment cardinality grows. The next post picks up exactly there — what to do once one column would explode into thousands.

This post is the companion to Video 1 in the preprocessing series: One-Hot Encoding.

Comments

Popular posts from this blog

READ vs SQL SELECT, A Quick Performance Test

All about READ in RPGLE & Why we use it with SETLL/SETGT?

Retrieve list of Spooled files on System from SQL - IBM i