Datos Categóricos
Aprende a codificar datos categóricos en Python con Label Encoding, Ordinal Encoding, One-Hot Encoding y pd.get_dummies con ejemplos de scikit-learn.
Los datos categóricos son cualquier dato que toma un conjunto limitado y fijo de valores — piensa en "red", "blue", "green" para una columna de colores, o en "low", "medium", "high" para una clasificación de severidad. La mayoría de los algoritmos de aprendizaje automático trabajan con números, por lo que las columnas categóricas deben convertirse a una representación numérica antes del entrenamiento.
Este capítulo explica las principales estrategias de codificación, cuándo elegir cada una y cómo aplicarlas correctamente en Python usando pandas y scikit-learn sin filtrar información del conjunto de prueba hacia tu modelo.
Por Qué Importa la Codificación
Pasar valores de string sin procesar a un estimador de scikit-learn genera un ValueError. Incluso cuando una columna ya contiene números — como 1, 2, 3 que representan "small", "medium", "large" — un algoritmo que trata los valores de las características como números continuos inferirá una relación falsa (por ejemplo, que "large" es tres veces "small"). La codificación te permite representar la relación real con precisión.
La elección de la codificación depende de dos preguntas:
- ¿Existe un orden natural? El color no tiene un orden natural (nominal). La talla de camiseta tiene un orden natural (ordinal). La codificación correcta preserva el orden cuando existe y lo ignora cuando no lo tiene.
- ¿Cuántos valores distintos (cardinalidad) tiene la columna? Las columnas de alta cardinalidad (cientos de ciudades únicas, IDs de productos) pueden crear miles de columnas dummy con One-Hot Encoding, lo que perjudica tanto la memoria como el rendimiento del modelo.
Configuración de un Conjunto de Datos de Ejemplo
Los ejemplos a continuación usan un pequeño conjunto de datos de ropa para que puedas seguir la salida exactamente.
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)Salida:
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 reemplaza cada categoría con un entero. El LabelEncoder de scikit-learn asigna enteros alfabéticamente.
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_))Salida:
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 (orden alfabético).
Cuándo usarlo: Label Encoding está pensado para la variable objetivo (y), no para las características de entrada. Aplicado a una columna de características nominales, los enteros codificados implican un ordenamiento que no existe, lo que confunde a los modelos basados en árboles y es perjudicial para los modelos lineales.
Revertir la codificación:
decoded = le.inverse_transform([0, 1, 2])
print(decoded) # ['blue' 'green' 'red']Ordinal Encoding
Ordinal Encoding es como Label Encoding pero te permite especificar el orden exacto de las categorías. Úsalo para características donde el orden es significativo.
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"]])Salida:
size size_encoded
0 S 0.0
1 M 1.0
2 L 2.0
3 S 0.0
4 M 1.0El modelo ahora puede inferir correctamente que L (2) > M (1) > S (0).
Manejo de categorías desconocidas en el momento de la predicción:
# 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 crea una columna binaria por categoría. Un 1 en una columna significa que la fila pertenece a esa categoría; todas las demás columnas son 0. Esta es la opción estándar para características nominales (sin orden) que se introducen en modelos lineales, SVMs y redes neuronales.
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)Salida:
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' rellena las categorías no vistas con todos ceros en lugar de generar un error cuando llegan nuevos datos en el momento de la predicción.
Eliminar una Columna para Evitar la Multicolinealidad
Con tres categorías se obtienen tres columnas binarias, pero la tercera es completamente predecible a partir de las otras dos (blue = 1 − green − red). Esta trampa de las variables dummy puede causar problemas en los modelos lineales. Elimina una columna con 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))Salida (una columna eliminada):
color_green color_red
0 0 1
1 1 0
2 0 0
3 1 0
4 0 1Los modelos basados en árboles (árboles de decisión, bosques aleatorios, gradient boosting) son inmunes a la trampa de las variables dummy, por lo que eliminar una columna es opcional para ellos.
pd.get_dummies — La Alternativa Rápida de Pandas
Para trabajo exploratorio, pd.get_dummies() es la forma más rápida de aplicar one-hot encoding a un DataFrame:
dummies = pd.get_dummies(df[["color", "size"]], dtype=int)
print(dummies)Salida:
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 0Limitación: pd.get_dummies() no es un transformador ajustado. No puede garantizar el mismo conjunto de columnas entre las divisiones de entrenamiento y prueba, y no admite handle_unknown. Para pipelines de producción, prefiere OneHotEncoder dentro de un Pipeline de scikit-learn.
Cómo Evitar la Fuga de Datos
La fuga de datos ocurre cuando la información del conjunto de prueba influye en cómo se preparan los datos de entrenamiento. El resultado es una puntuación de evaluación excesivamente optimista que no refleja el rendimiento en el mundo real.
El patrón correcto es:
- Dividir los datos en conjuntos de entrenamiento y prueba primero.
- Ajustar cualquier codificador solo con el conjunto de entrenamiento.
- Usar
transform()(nofit_transform()) en el conjunto de prueba.
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 más detalles sobre la división en entrenamiento y prueba, consulta el capítulo Train/Test Split.
Uso de un Pipeline para Combinar la Codificación con un Modelo
Un Pipeline de scikit-learn encadena un transformador y un estimador. Esto garantiza que el codificador siempre se ajuste solo con los datos de entrenamiento, incluso durante la validación 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))El ColumnTransformer aplica diferentes pasos de preprocesamiento a distintas columnas en un solo paso. El pipeline es el patrón recomendado para todos los flujos de trabajo de aprendizaje automático de nivel productivo.
Cómo Elegir la Codificación Correcta
| Situación | Codificación recomendada |
|---|---|
| Variable objetivo (y) | LabelEncoder |
| Característica ordinal (existe orden natural) | OrdinalEncoder con categories explícitas |
| Característica nominal, baja cardinalidad | OneHotEncoder (o pd.get_dummies para exploración) |
| Característica nominal, alta cardinalidad | Target encoding o frequency encoding (ver nota abajo) |
| Pipeline de producción | OneHotEncoder dentro de un Pipeline / ColumnTransformer |
Target encoding reemplaza cada categoría con la media de la variable objetivo para esa categoría. Maneja bien la alta cardinalidad pero es especialmente propenso a la fuga de datos — aplícalo siempre con pliegues de validación cruzada o usa una implementación de biblioteca (p. ej., category_encoders.TargetEncoder) que lo gestione automáticamente.
Capítulos Relacionados
- Scale — normaliza y estandariza características numéricas antes del modelado
- Train/Test Split — divide los datos correctamente antes de cualquier paso de preprocesamiento
- Linear Regression — un modelo que se beneficia de una codificación categórica adecuada
- Cross Validation — evalúa modelos de forma fiable cuando se combina con pipelines de codificación
- Confusion Matrix — mide el rendimiento del modelo de clasificación tras codificar los objetivos
- Pandas Tutorial — fundamentos de pandas, incluyendo la creación y manipulación de DataFrames