Dados Categóricos
Aprenda a codificar dados categóricos em Python com Label Encoding, Ordinal Encoding, One-Hot Encoding e pd.get_dummies com exemplos do scikit-learn.
Dados categóricos são quaisquer dados que assumem um conjunto limitado e fixo de valores — pense em "red", "blue", "green" para uma coluna de cor, ou "low", "medium", "high" para uma classificação de gravidade. A maioria dos algoritmos de machine learning trabalha com números, portanto as colunas categóricas devem ser convertidas para uma representação numérica antes do treinamento.
Este capítulo explica as principais estratégias de codificação, quando escolher cada uma delas e como aplicá-las corretamente em Python usando pandas e scikit-learn sem vazar informações do conjunto de teste para o seu modelo.
Por que a Codificação é Importante
Fornecer valores de string brutos a um estimador do scikit-learn gera um ValueError. Mesmo quando uma coluna já contém números — como 1, 2, 3 representando "small", "medium", "large" — um algoritmo que trata os valores de features como números contínuos inferirá um relacionamento falso (por exemplo, "large" é três vezes maior que "small"). A codificação permite representar o relacionamento real com precisão.
A escolha da codificação depende de duas perguntas:
- Existe uma ordem natural? Cor não tem ordem natural (nominal). Tamanho de camiseta tem uma ordem natural (ordinal). A codificação correta preserva a ordem quando ela existe e a ignora quando não existe.
- Quantos valores distintos (cardinalidade) a coluna possui? Colunas de alta cardinalidade (centenas de cidades únicas, IDs de produtos) podem criar milhares de colunas dummy com One-Hot Encoding, o que prejudica tanto a memória quanto o desempenho do modelo.
Configurando um Dataset de Exemplo
Os exemplos abaixo utilizam um pequeno dataset de vestuário para que você possa acompanhar a saída exatamente.
import pandas as pd
data = {
"color": ["red", "green", "blue", "green", "red"],
"size": ["S", "M", "L", "S", "M"],
"price": [10, 20, 30, 10, 20],
"in_stock": [True, True, False, True, False],
}
df = pd.DataFrame(data)
print(df)Saída:
color size price in_stock
0 red S 10 True
1 green M 20 True
2 blue L 30 False
3 green S 10 True
4 red M 20 FalseLabel Encoding
Label Encoding substitui cada categoria por um inteiro. O LabelEncoder do scikit-learn atribui inteiros em ordem alfabética.
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df["color_encoded"] = le.fit_transform(df["color"])
print(df[["color", "color_encoded"]])
print("Classes:", list(le.classes_))Saída:
color color_encoded
0 red 2
1 green 1
2 blue 0
3 green 1
4 red 2
Classes: ['blue', 'green', 'red']blue → 0, green → 1, red → 2 (ordem alfabética).
Quando usar: Label Encoding é destinado à variável alvo (y), não às features de entrada. Aplicado a uma coluna de feature nominal, os inteiros codificados implicam uma ordenação que não existe, o que induz a erros em modelos baseados em árvores e é prejudicial para modelos lineares.
Revertendo a codificação:
decoded = le.inverse_transform([0, 1, 2])
print(decoded) # ['blue' 'green' 'red']Ordinal Encoding
Ordinal Encoding é semelhante ao Label Encoding, mas permite especificar a ordem exata das categorias. Use-o para features em que a ordem é significativa.
from sklearn.preprocessing import OrdinalEncoder
# Define the order explicitly: S < M < L
oe = OrdinalEncoder(categories=[["S", "M", "L"]])
df["size_encoded"] = oe.fit_transform(df[["size"]])
print(df[["size", "size_encoded"]])Saída:
size size_encoded
0 S 0.0
1 M 1.0
2 L 2.0
3 S 0.0
4 M 1.0O modelo agora pode inferir corretamente que L (2) > M (1) > S (0).
Tratando categorias desconhecidas no momento da predição:
# Use handle_unknown='use_encoded_value' with unknown_value=-1
oe_safe = OrdinalEncoder(
categories=[["S", "M", "L"]],
handle_unknown="use_encoded_value",
unknown_value=-1,
)
oe_safe.fit(df[["size"]])
print(oe_safe.transform([["XL"]])) # [[-1.]]One-Hot Encoding
One-Hot Encoding cria uma coluna binária por categoria. Um 1 em uma coluna indica que a linha pertence àquela categoria; todas as outras colunas recebem 0. Esta é a escolha padrão para features nominais (sem ordem) fornecidas a modelos lineares, SVMs e redes neurais.
from sklearn.preprocessing import OneHotEncoder
import numpy as np
ohe = OneHotEncoder(sparse_output=False, handle_unknown="ignore")
color_encoded = ohe.fit_transform(df[["color"]])
# Build a labelled DataFrame from the result
col_names = ohe.get_feature_names_out(["color"])
color_df = pd.DataFrame(color_encoded, columns=col_names, dtype=int)
print(color_df)Saída:
color_blue color_green color_red
0 0 0 1
1 0 1 0
2 1 0 0
3 0 1 0
4 0 0 1handle_unknown='ignore' preenche categorias não vistas com zeros em vez de gerar um erro quando novos dados chegam no momento da predição.
Removendo uma Coluna para Evitar Multicolinearidade
Com três categorias você obtém três colunas binárias, mas a terceira é totalmente previsível a partir das outras duas (blue = 1 − green − red). Essa armadilha das variáveis dummy pode causar problemas em modelos lineares. Remova uma coluna com drop='first':
ohe_nodrop = OneHotEncoder(sparse_output=False, drop="first", handle_unknown="ignore")
reduced = ohe_nodrop.fit_transform(df[["color"]])
print(pd.DataFrame(reduced, columns=ohe_nodrop.get_feature_names_out(["color"]), dtype=int))Saída (uma coluna removida):
color_green color_red
0 0 1
1 1 0
2 0 0
3 1 0
4 0 1Modelos baseados em árvores (árvores de decisão, florestas aleatórias, gradient boosting) são imunes à armadilha das variáveis dummy, portanto remover uma coluna é opcional para eles.
pd.get_dummies — A Alternativa Rápida do Pandas
Para trabalho exploratório, pd.get_dummies() é a maneira mais rápida de aplicar one-hot encoding a um DataFrame:
dummies = pd.get_dummies(df[["color", "size"]], dtype=int)
print(dummies)Saída:
color_blue color_green color_red size_L size_M size_S
0 0 0 1 0 0 1
1 0 1 0 0 1 0
2 1 0 0 1 0 0
3 0 1 0 0 0 1
4 0 0 1 0 1 0Limitação: pd.get_dummies() não é um transformador ajustável. Não pode garantir o mesmo conjunto de colunas entre divisões de treinamento e teste, e não suporta handle_unknown. Para pipelines em produção, prefira OneHotEncoder dentro de um Pipeline do scikit-learn.
Evitando Vazamento de Dados
Vazamento de dados ocorre quando informações do conjunto de teste influenciam a forma como os dados de treinamento são preparados. O resultado é uma pontuação de avaliação excessivamente otimista que não reflete o desempenho no mundo real.
O padrão correto é:
- Divida os dados em conjuntos de treinamento e teste primeiro.
- Ajuste qualquer codificador apenas no conjunto de treinamento.
- Use
transform()(nãofit_transform()) no conjunto de teste.
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
X = df[["color", "size"]]
y = df["price"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42)
ohe = OneHotEncoder(sparse_output=False, handle_unknown="ignore")
ohe.fit(X_train) # fit on training data only
X_train_enc = ohe.transform(X_train) # transform training set
X_test_enc = ohe.transform(X_test) # transform test set using training-fit encoderPara mais detalhes sobre a divisão treino/teste, consulte o capítulo Train/Test Split.
Usando um Pipeline para Combinar Codificação com um Modelo
Um Pipeline do scikit-learn encadeia um transformador e um estimador. Isso garante que o codificador seja sempre ajustado apenas nos dados de treinamento, inclusive durante a validação cruzada.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.linear_model import LinearRegression
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import train_test_split
categorical_cols = ["color", "size"]
numeric_cols = ["in_stock"]
X_full = df[categorical_cols + numeric_cols]
y_full = df["price"]
X_tr, X_te, y_tr, y_te = train_test_split(X_full, y_full, test_size=0.4, random_state=42)
preprocessor = ColumnTransformer(transformers=[
("ohe", OneHotEncoder(handle_unknown="ignore"), categorical_cols),
("pass", "passthrough", numeric_cols),
])
pipe = Pipeline(steps=[
("preprocessor", preprocessor),
("model", LinearRegression()),
])
pipe.fit(X_tr, y_tr)
print("Test predictions:", pipe.predict(X_te))O ColumnTransformer aplica diferentes etapas de pré-processamento a colunas diferentes em uma única passagem. O pipeline é o padrão recomendado para todos os fluxos de trabalho de machine learning em nível de produção.
Escolhendo a Codificação Correta
| Situação | Codificação recomendada |
|---|---|
| Variável alvo (y) | LabelEncoder |
| Feature ordinal (ordem natural existe) | OrdinalEncoder com categories explícito |
| Feature nominal, baixa cardinalidade | OneHotEncoder (ou pd.get_dummies para exploração) |
| Feature nominal, alta cardinalidade | Target encoding ou frequency encoding (veja nota abaixo) |
| Pipeline de produção | OneHotEncoder dentro de um Pipeline / ColumnTransformer |
Target encoding substitui cada categoria pela média da variável alvo para aquela categoria. Lida bem com alta cardinalidade, mas é especialmente propenso ao vazamento de dados — sempre aplique-o com folds de validação cruzada ou use uma implementação de biblioteca (por exemplo, category_encoders.TargetEncoder) que trate isso automaticamente.
Capítulos Relacionados
- Scale — normalize e padronize features numéricas antes da modelagem
- Train/Test Split — divida os dados corretamente antes de qualquer etapa de pré-processamento
- Linear Regression — um modelo que se beneficia de uma codificação categórica adequada
- Cross Validation — avalie modelos de forma confiável quando combinado com pipelines de codificação
- Confusion Matrix — meça o desempenho de modelos de classificação após codificar os alvos
- Pandas Tutorial — fundamentos do pandas incluindo criação e manipulação de DataFrames