Dati Categoriali
Impara a codificare i dati categoriali in Python con Label Encoding, Ordinal Encoding, One-Hot Encoding e pd.get_dummies con esempi scikit-learn.
I dati categoriali sono qualsiasi tipo di dato che assume un insieme limitato e fisso di valori — come "red", "blue", "green" per una colonna del colore, oppure "low", "medium", "high" per una valutazione di gravità. La maggior parte degli algoritmi di machine learning lavora con numeri, quindi le colonne categoriali devono essere convertite in una rappresentazione numerica prima dell'addestramento.
Questo capitolo illustra le principali strategie di codifica, quando scegliere ciascuna e come applicarle correttamente in Python usando pandas e scikit-learn senza far trapelare informazioni dal set di test nel modello.
Perché la Codifica è Importante
Fornire valori stringa grezzi a uno stimatore scikit-learn genera un ValueError. Anche quando una colonna contiene già numeri — come 1, 2, 3 che rappresentano "small", "medium", "large" — un algoritmo che tratta i valori delle feature come numeri continui inferirà una relazione falsa (ad es., "large" è tre volte "small"). La codifica permette di rappresentare accuratamente la vera relazione.
La scelta della codifica dipende da due domande:
- Esiste un ordine naturale? Il colore non ha un ordine naturale (nominale). La taglia di una maglietta ha un ordine naturale (ordinale). La codifica corretta preserva l'ordine quando esiste e lo ignora quando non esiste.
- Quanti valori distinti (cardinalità) ha la colonna? Le colonne ad alta cardinalità (centinaia di città uniche, ID prodotto) possono creare migliaia di colonne dummy con il One-Hot Encoding, penalizzando sia la memoria che le prestazioni del modello.
Impostare un Dataset di Esempio
Gli esempi seguenti utilizzano un piccolo dataset di abbigliamento così da poter seguire l'output con precisione.
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)Output:
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
Il Label Encoding sostituisce ogni categoria con un intero. Il LabelEncoder di scikit-learn assegna gli interi in ordine alfabetico.
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_))Output:
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 (ordine alfabetico).
Quando usarlo: il Label Encoding è pensato per la variabile target (y), non per le feature di input. Applicato a una colonna di feature nominale, gli interi codificati implicano un ordinamento che non esiste, il che fuorvia i modelli basati su alberi ed è dannoso per i modelli lineari.
Invertire la codifica:
decoded = le.inverse_transform([0, 1, 2])
print(decoded) # ['blue' 'green' 'red']Ordinal Encoding
L'Ordinal Encoding è simile al Label Encoding ma consente di specificare l'ordine esatto delle categorie. Usalo per le feature in cui l'ordine è 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"]])Output:
size size_encoded
0 S 0.0
1 M 1.0
2 L 2.0
3 S 0.0
4 M 1.0Il modello può ora inferire correttamente che L (2) > M (1) > S (0).
Gestione delle categorie sconosciute al momento della predizione:
# 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
Il One-Hot Encoding crea una colonna binaria per ogni categoria. Un 1 in una colonna indica che la riga appartiene a quella categoria; tutte le altre colonne sono 0. Questa è la scelta standard per le feature nominali (non ordinate) fornite a modelli lineari, SVM e reti neurali.
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)Output:
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' riempie le categorie non viste con tutti zeri invece di generare un errore quando arrivano nuovi dati al momento della predizione.
Eliminare una Colonna per Evitare la Multicollinearità
Con tre categorie si ottengono tre colonne binarie, ma la terza è completamente predicibile dalle altre due (blue = 1 − green − red). Questa trappola della variabile dummy può causare problemi nei modelli lineari. Elimina una colonna 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))Output (una colonna eliminata):
color_green color_red
0 0 1
1 1 0
2 0 0
3 1 0
4 0 1I modelli basati su alberi (alberi decisionali, random forest, gradient boosting) sono immuni alla trappola della variabile dummy, quindi eliminare una colonna è facoltativo per loro.
pd.get_dummies — L'Alternativa Rapida di Pandas
Per l'analisi esplorativa, pd.get_dummies() è il modo più veloce per applicare il one-hot encoding a un DataFrame:
dummies = pd.get_dummies(df[["color", "size"]], dtype=int)
print(dummies)Output:
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 0Limitazione: pd.get_dummies() non è un trasformatore addestrato. Non può garantire lo stesso insieme di colonne tra le suddivisioni di training e test, e non supporta handle_unknown. Per le pipeline di produzione, preferire OneHotEncoder all'interno di una Pipeline di scikit-learn.
Evitare il Data Leakage
Il data leakage si verifica quando le informazioni del set di test influenzano la preparazione dei dati di addestramento. Il risultato è un punteggio di valutazione eccessivamente ottimistico che non riflette le prestazioni reali.
Il pattern corretto è:
- Dividere i dati in set di training e test prima.
- Addestrare qualsiasi encoder solo sul set di training.
- Usare
transform()(nonfit_transform()) sul set di test.
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 encoderPer i dettagli sulla suddivisione training/test, consulta il capitolo Train/Test Split.
Utilizzare una Pipeline per Combinare la Codifica con un Modello
Una Pipeline di scikit-learn collega un trasformatore e uno stimatore. Questo garantisce che l'encoder venga sempre addestrato solo sui dati di training, anche durante la cross-validation.
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))Il ColumnTransformer applica fasi di preprocessing diverse a colonne diverse in un'unica passata. La pipeline è il pattern raccomandato per tutti i flussi di lavoro di machine learning in produzione.
Scegliere la Codifica Giusta
| Situazione | Codifica consigliata |
|---|---|
| Variabile target (y) | LabelEncoder |
| Feature ordinale (ordine naturale esistente) | OrdinalEncoder con categories esplicite |
| Feature nominale, bassa cardinalità | OneHotEncoder (o pd.get_dummies per l'esplorazione) |
| Feature nominale, alta cardinalità | Target encoding o frequency encoding (vedi nota sotto) |
| Pipeline di produzione | OneHotEncoder dentro una Pipeline / ColumnTransformer |
Il target encoding sostituisce ogni categoria con la media della variabile target per quella categoria. Gestisce bene l'alta cardinalità ma è particolarmente soggetto al data leakage — applicarlo sempre con fold di cross-validation oppure usare un'implementazione di libreria (es. category_encoders.TargetEncoder) che lo gestisce automaticamente.
Capitoli Correlati
- Scale — normalizzare e standardizzare le feature numeriche prima della modellazione
- Train/Test Split — suddividere correttamente i dati prima di qualsiasi fase di preprocessing
- Linear Regression — un modello che trae vantaggio dalla corretta codifica categoriale
- Cross Validation — valutare i modelli in modo affidabile quando combinati con pipeline di codifica
- Confusion Matrix — misurare le prestazioni del modello di classificazione dopo la codifica dei target
- Pandas Tutorial — fondamenti di pandas incluse la creazione e la manipolazione dei DataFrame