Python Tutorial
Categorical Data
Turn text categories into numbers that models can use — without inventing a false ordering.
Why Encoding Is Needed
Most models do arithmetic on their inputs, so a column like Color = ["red", "green", "blue"] must become numeric. The trick is to encode categories without implying an order that does not exist — "blue" is not greater than "red".
One-Hot Encoding
One-hot encoding creates a separate 0/1 column per category. It is the safe default for nominal (unordered) features.
import pandas as pd
df = pd.DataFrame({
"Color": ["red", "green", "blue", "green"],
"Price": [10, 15, 20, 12],
})
encoded = pd.get_dummies(df, columns=["Color"], drop_first=False)
print(encoded)
# Price Color_blue Color_green Color_red
# 0 10 False False True
# ...drop_first=True removes one column to avoid the "dummy variable trap" (perfect collinearity) in linear models. Tree models do not need this.
Ordinal Encoding
When categories have a genuine order — ["low", "medium", "high"] — map them to ranked integers so the model can use the ordering.
from sklearn.preprocessing import OrdinalEncoder
sizes = [["low"], ["high"], ["medium"], ["low"]]
enc = OrdinalEncoder(categories=[["low", "medium", "high"]])
print(enc.fit_transform(sizes).ravel()) # [0. 2. 1. 0.]Never apply ordinal encoding to unordered categories. Telling the model red=0, green=1, blue=2 invents a ranking that will distort distance and linear models.
Encoding in a scikit-learn Pipeline
ColumnTransformer applies different encoders to different columns and remembers the categories learned on the training set — essential for correctly transforming new data.
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LinearRegression
pre = ColumnTransformer([
("cat", OneHotEncoder(handle_unknown="ignore"), ["Color"]),
("num", StandardScaler(), ["Price"]),
])
model = Pipeline([("pre", pre), ("reg", LinearRegression())])
model.fit(df[["Color", "Price"]], [1, 2, 3, 2])handle_unknown="ignore" keeps the pipeline from crashing when the test set contains a category the training set never saw.
High-Cardinality Features
One-hot encoding a column with thousands of categories (zip codes, product IDs) explodes the feature count. Alternatives:
- Target / mean encoding: replace each category with the mean target value (fit inside CV to avoid leakage).
- Frequency encoding: replace each category with how often it appears.
- Hashing / embeddings: map categories into a fixed-size space.
Best Practices
- One-hot for nominal features; ordinal only when a real order exists.
- Fit the encoder on training data and reuse it on test/new data.
- Use
handle_unknown="ignore"so unseen categories do not break inference. - Prefer target/frequency encoding for very high-cardinality columns.
Try It Yourself
Exercise 1: One-hot encode a column of colours with pandas.
Show solution
import pandas as pd
df = pd.DataFrame({"color": ["red", "blue", "red"]})
print(pd.get_dummies(df, columns=["color"]))Exercise 2: Would you ordinal-encode ["small", "medium", "large"] or one-hot encode it? Why?
Show solution
Ordinal encoding — the categories have a genuine order (small < medium < large) that the model can use.
📘 Real-World Deep Dive
Knowing <strong>ML Categorical Data (scikit-learn)</strong> well is what turns scikit-learn from a curiosity into a daily tool — you'll reach for it in nearly every real project.
Real-Life Scenario
An end-to-end usage of ML Categorical Data that you'd actually see in a data pipeline or analytics notebook.
Real-Life Example
import numpy as np
from sklearn.preprocessing import OneHotEncoder
X = np.array([["red"], ["blue"], ["red"]])
print(OneHotEncoder().fit_transform(X).toarray())Expected Output
(see source)Common mistakes
fitexpects numeric arrays;OneHotEncoder/LabelEncoderare easy to forget for categorical features.- Calling
predicton a model trained on unscaled data and then scaling inputs at inference time silently degrades accuracy. train_test_split(X, y)requires both arrays;train_test_split(X)for unsupervised learning slips past static checkers.- Treating ML Categorical Data as a black box without reading the docs — the API has subtle defaults that bite when you scale.
🚀 Performance & Best Practices
- Wrap preprocessing + estimator in a
Pipelinesofit/predictstay reproducible. - Use
joblib/picklefor serialising models, not the entire Python state. - For > 100 k rows, switch to
HistGradientBoostingClassifierorcuML. - When working with scikit-learn, prefer vectorised / batched operations over Python loops.
🧪 Try It Yourself
- Reproduce the snippet on a representative slice of your own data.
- Profile the snippet with
cProfileortimeitand find the single biggest improvement. - Generalise the snippet into a small, reusable function you can drop into future projects.