Encoding categorical features

scikit-learn · Essentials · 10 min

Most estimators accept only numbers, and real tabular data is full of categories.

Columns such as city or plan hold categories, which most estimators cannot use directly. Two encoders cover most needs.

OneHotEncoder creates one 0/1 column per category. It makes no assumption of order, so it is the safe default for nominal features with a modest number of categories. Key options:

  • handle_unknown="ignore": a category not seen in training becomes all zeros instead of raising an error. Use "infrequent_if_exist" together with min_frequency or max_categories to group rare categories.
  • sparse_output=False: return a dense array (the default is a sparse matrix).
  • drop="if_binary" or drop="first": avoid a redundant column, mostly useful for unregularised linear models.

get_feature_names_out() tells you the names of the generated columns, like city_Paris.

OrdinalEncoder maps categories to integers 0, 1, 2... It is compact and suits tree-based models, or genuinely ordered categories when you pass the order via categories=[["low", "mid", "high"]]. For linear models, arbitrary integer codes imply a false ordering.

LabelEncoder is meant for the target y, not for features. Like scalers, encoders must be fitted on the training data, ideally inside a pipeline.

Example

import pandas as pd
from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder

train = pd.DataFrame({"city": ["Paris", "Rome", "Paris", "Oslo"],
                      "size": ["S", "L", "M", "S"]})
test = pd.DataFrame({"city": ["Rome", "Lima"], "size": ["M", "L"]})

ohe = OneHotEncoder(handle_unknown="ignore", sparse_output=False)
ohe.fit(train[["city"]])
print(ohe.get_feature_names_out())    # ['city_Oslo' 'city_Paris' 'city_Rome']
print(ohe.transform(test[["city"]]))  # Lima is unseen -> all zeros
# [[0. 0. 1.]
#  [0. 0. 0.]]

ord_enc = OrdinalEncoder(categories=[["S", "M", "L"]])
print(ord_enc.fit_transform(train[["size"]]).ravel())  # [0. 2. 1. 0.]

Remember

  • One-hot for nominal categories, ordinal for ordered ones or for trees
  • handle_unknown="ignore" keeps prediction working on unseen categories
  • LabelEncoder is for targets, not features

Watch out for

  • Using pd.get_dummies separately on train and test, which can produce different columns.

Check yourself

  1. At prediction time a category appears that was not in the training data. With the default OneHotEncoder settings, what happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: scikit-learn.org/stable/modules/preprocessing.html#encoding-categorical-features