Le preprocessing des données est une étape critique en Machine Learning, souvent sous-estimée mais déterminante pour la performance d'un modèle. Une des transformations les plus fondamentales consiste à convertir les variables catégorielles (texte) en variables numériques (nombres). Pourquoi ? Parce que les algorithmes de Machine Learning — qu'il s'agisse de régression linéaire, de forêts aléatoires ou de réseaux de neurones — ne manipulent que des tenseurs numériques. Ils ne peuvent pas "comprendre" directement qu'un arbre est "Mature" ou "Jeune".
Dans ce guide, nous allons analyser en profondeur trois techniques d'encodage appliquées à un dataset réel de 42 588 arbres parisiens (Platanes), en expliquant les fondements théoriques, les pièges à éviter, et les bonnes pratiques industrielles.
Le Dataset : Arbres Remarquables de Paris
Nous travaillons sur un sous-ensemble du patrimoine arboré parisien, filtré sur les Platanes uniquement. Ce dataset contient plusieurs variables catégorielles représentatives des défis courants en Data Science :
remarquable: Variable binaire (OUI/NON) indiquant si l'arbre a un intérêt historique ou esthétique exceptionnelstade_de_developpement: Variable ordinale (Jeune → Adulte → Mature) avec une progression logiqueespece: Variable nominale (Platane, Chêne, Érable, etc.) sans hiérarchie naturelle
Ce cas d'usage est idéal pour illustrer les trois familles d'encodage : binaire, ordinal et nominal.
Problématique Technique : Pourquoi les Catégories Doivent Devenir des Nombres
L'Incompatibilité Fondamentale
Les algorithmes ML reposent sur des opérations mathématiques : multiplication matricielle, calcul de distances euclidiennes, gradients de fonctions de coût. Ces opérations nécessitent des valeurs numériques.
Exemple concret : Si on entraîne un modèle de régression pour prédire la hauteur d'un arbre à partir de son stade de développement, l'algorithme a besoin de calculer :
Hauteur prédite = w₁ × stade + w₂ × circonférence + b
Si stade = "Adulte" (chaîne de caractères), cette équation n'a aucun sens mathématique. Il faut transformer "Adulte" en un nombre (par exemple, 3).
Le Piège de l'Encodage Naïf
La solution la plus simple serait d'assigner arbitrairement :
- Jeune → 1
- Adulte → 2
- Mature → 3
Mais cette approche fonctionne uniquement si l'ordre a du sens (variables ordinales). Pour des catégories nominales comme les espèces d'arbres, cela créerait une hiérarchie artificielle que le modèle pourrait mal interpréter. Par exemple, si Chêne=1, Érable=2, Platane=3, le modèle pourrait "apprendre" que Platane = Chêne + Érable, ce qui est absurde.
Technique 1 : Encodage Binaire Manuel (0/1) — La Variable remarquable
Contexte et Distribution
La variable remarquable est une variable binaire : un arbre est soit remarquable (OUI), soit ordinaire (NON). C'est le cas le plus simple d'encodage catégoriel.
Distribution observée :
- NON : 42 493 arbres (99.8%)
- OUI : 95 arbres (0.2%)
Cette distribution déséquilibrée (class imbalance) est typique des problèmes de classification d'événements rares (fraude bancaire, pannes industrielles, etc.).
Méthode d'Encodage
L'encodage manuel est direct et explicite :
df.loc[df.remarquable == "NON", "remarquable"] = 0
df.loc[df.remarquable == "OUI", "remarquable"] = 1
Pourquoi cette méthode ?
- Sémantique claire : 0 = absence de propriété, 1 = présence. Cette convention est universelle.
- Performance : Pas besoin d'instancier un encodeur complexe pour deux valeurs.
- Interprétabilité : Dans un modèle de régression logistique, le coefficient associé aura une interprétation directe (impact sur le log-odds).
Analogie technique : C'est l'équivalent d'un bit en informatique — un interrupteur qui ne peut être que OFF (0) ou ON (1).
Avantages et Limites
✅ Avantages :
- Simple et lisible
- Aucune dépendance externe (pas besoin de bibliothèques spécialisées)
- Économie de mémoire : un seul entier par ligne
❌ Limites :
- Ne généralise pas aux variables avec plus de deux catégories
- Nécessite une gestion manuelle des nouvelles données
Technique 2 : Encodage Ordinal — La Variable stade_de_developpement
Théorie des Variables Ordinales
Une variable ordinale est une catégorie où l'ordre relatif a une signification intrinsèque. Le stade de développement d'un arbre suit une progression biologique naturelle :
Ø (manquant) → Jeune → Jeune/Adulte → Adulte → Mature
Cette séquence est non interchangeable : un arbre ne peut pas être "Mature" avant d'avoir été "Jeune". C'est différent d'une variable nominale (espèce) où aucun ordre n'existe.
Analogie : Les niveaux scolaires (Maternelle → Primaire → Collège → Lycée) suivent une progression ordonnée. On ne peut pas être au lycée avant d'avoir terminé le collège.
Implémentation avec OrdinalEncoder
L'utilisation de category_encoders.OrdinalEncoder offre plusieurs avantages sur un mapping manuel :
from category_encoders.ordinal import OrdinalEncoder
mapping = [
{
"col": "stade_de_developpement",
"mapping": {
np.nan: 0, # Valeur manquante
"Jeune (arbre)": 1,
"Jeune (arbre)Adulte": 2,
"Adulte": 3,
"Mature": 4,
},
}
]
encoder = OrdinalEncoder(mapping=mapping)
stade = encoder.fit_transform(df[["stade_de_developpement"]])
Gestion des Valeurs Manquantes
Un point critique : la présence de np.nan (valeurs manquantes). Dans ce dataset, certains arbres n'ont pas de stade renseigné. L'encodeur ordinal les mappe explicitement à 0, ce qui permet :
- Conservation des données : On ne perd pas les lignes incomplètes
- Distinction sémantique : 0 = "information inconnue", différent de 1 = "Jeune"
- Compatibilité algorithmique : La plupart des algorithmes ML gèrent les 0, mais pas les
NaN
Bonnes pratiques :
- Toujours vérifier
value_counts(dropna=False)avant et après encodage - Documenter explicitement la signification de la valeur 0
- Envisager des stratégies alternatives (imputation par la médiane, modèle prédictif) si les valeurs manquantes représentent >20% des données
Application au DataFrame
Un piège classique pour les débutants est d'oublier d'intégrer l'encodage au DataFrame original :
# ❌ Erreur : l'encodage reste isolé
stade = encoder.fit_transform(df[["stade_de_developpement"]])
# ✅ Correct : intégration au DataFrame
df["stade_de_developpement"] = stade["stade_de_developpement"].copy()
Pourquoi .copy() ? Pour éviter les avertissements SettingWithCopyWarning de Pandas et garantir une copie mémoire indépendante.
Analogie : Remplacer le moteur d'une voiture. Ce n'est pas suffisant de construire un nouveau moteur à côté ; il faut l'installer dans le châssis pour que la voiture fonctionne.
Technique 3 : Encodage Binaire (Binary Encoding) — La Variable espece
Le Défi des Variables Nominales à Haute Cardinalité
La variable espece présente un problème différent : il n'y a aucun ordre naturel entre un Platane, un Chêne ou un Érable. De plus, le dataset complet contient des dizaines voire centaines d'espèces différentes (haute cardinalité).
Pourquoi One-Hot Encoding N'est Pas Optimal Ici
La technique classique pour les variables nominales est le One-Hot Encoding :
- Chêne →
[1, 0, 0] - Érable →
[0, 1, 0] - Platane →
[0, 0, 1]
Mais si on a 100 espèces, cela crée 100 colonnes dans le DataFrame, ce qui :
- Augmente drastiquement la dimensionnalité (curse of dimensionality)
- Crée une matrice creuse (sparse matrix) coûteuse en mémoire
- Ralentit l'entraînement des modèles
Binary Encoding : La Solution Compacte
Le Binary Encoding représente chaque catégorie en code binaire, comme en informatique :
Espèce A (1) → [0, 0, 0, 1]
Espèce B (2) → [0, 0, 1, 0]
Espèce C (3) → [0, 0, 1, 1]
Espèce D (4) → [0, 1, 0, 0]
...
Espèce 100 (100) → [1, 1, 0, 0, 1, 0, 0] # 2^7 = 128 > 100
Avantage majeur : Pour encoder N catégories, on n'a besoin que de log₂(N) colonnes.
- 100 espèces → 7 colonnes (car 2⁷ = 128)
- 1000 espèces → 10 colonnes (car 2¹⁰ = 1024)
Versus 100 ou 1000 colonnes avec One-Hot Encoding !
Implémentation
from category_encoders.binary import BinaryEncoder
encoder = BinaryEncoder()
espece_bin = encoder.fit_transform(data[["espece"]])
print(f"Shape: {espece_bin.shape}")
# Résultat : (207641, 10) → 10 colonnes pour toutes les espèces
Note importante : Dans notre notebook, on applique l'encodage sur data (dataset complet), pas sur df (uniquement les Platanes), car df ne contient qu'une seule espèce. Encoder une seule catégorie n'a aucun intérêt pratique — c'est une démonstration technique.
Analogie : Les Codes-Barres
Un code-barres sur un produit de supermarché est composé de barres noires (1) et blanches (0). Il identifie uniquement le produit, sans suggérer qu'un code 12345 est "meilleur" ou "plus grand" qu'un code 67890. C'est exactement ce que fait le Binary Encoding : identifier de manière unique sans créer de hiérarchie artificielle.
Tableau Récapitulatif : Quelle Technique Choisir ?
| Type de Variable | Exemple | Méthode Recommandée | Justification |
|---|---|---|---|
| Binaire | OUI/NON, Vrai/Faux | Encodage manuel (0/1) | Simple, sémantique claire, pas de dépendance |
| Ordinale | Jeune → Adulte → Mature | OrdinalEncoder | Préserve l'ordre naturel, gestion des valeurs manquantes |
| Nominale (peu de catégories < 10) | Couleurs (Rouge, Vert, Bleu) | One-Hot Encoding | Pas d'ordre, nombre limité de colonnes créées |
| Nominale (haute cardinalité > 50) | Espèces, Villes, Codes postaux | BinaryEncoder | Compact (log₂ colonnes), évite la matrice creuse, pas de hiérarchie |
Pièges Critiques à Éviter
1. Le Double Encodage
Erreur classique observée : Appliquer deux encodages successifs sur la même colonne.
# ❌ Double encodage (bug détecté dans la version initiale du notebook)
for category in df.stade_de_developpement.unique():
df.loc[df.stade_de_developpement == category, "stade_de_developpement"] = code
encoder = OrdinalEncoder(mapping=mapping)
stade = encoder.fit_transform(df[["stade_de_developpement"]])
Symptôme : Résultats aberrants (valeurs -1.0 ou 0.0 uniquement) parce que l'encodeur cherche les catégories textuelles originales, mais trouve des nombres déjà transformés.
Solution : Supprimer l'encodage manuel et n'utiliser que OrdinalEncoder.
2. Oublier l'Intégration au DataFrame
# ❌ Erreur fréquente
espece_encoded = encoder.fit_transform(df[["espece"]])
# df contient toujours du texte ! Le modèle ne verra jamais les valeurs numériques.
# ✅ Correction
espece_encoded = encoder.fit_transform(df[["espece"]])
df = pd.concat([df, espece_encoded], axis=1)
df.drop("espece", axis=1, inplace=True)
Règle d'or : Toute variable transformée qui doit servir de feature dans un modèle ML DOIT être intégrée au DataFrame final. C'est comme préparer une recette : on ne peut pas donner farine, œufs et sucre dans des bols séparés au four. Il faut tout mélanger dans un seul récipient.
3. Créer une Hiérarchie Artificielle pour les Variables Nominales
# ❌ Mauvaise pratique pour les espèces
df["espece"] = df["espece"].map({"Chêne": 1, "Érable": 2, "Platane": 3})
Le modèle pourrait interpréter mathématiquement que Platane (3) = Chêne (1) + Érable (2), ce qui n'a aucun sens biologique.
Solution : Utiliser One-Hot ou Binary Encoding pour les variables nominales.
Bonnes Pratiques Industrielles
Vérification Systématique Après Transformation
Toujours inspecter les résultats avec value_counts() :
print("Avant encodage :")
print(df.stade_de_developpement.value_counts(dropna=False))
# ... encodage ...
print("Après encodage :")
print(df.stade_de_developpement.value_counts())
Cela permet de détecter immédiatement les anomalies (valeurs inattendues, perte de données).
Sauvegarde des Encodeurs pour la Production
En production, les nouvelles données doivent être encodées exactement de la même manière que les données d'entraînement. Il faut donc sauvegarder les encodeurs :
import joblib
# Après entraînement
joblib.dump(encoder, "ordinal_encoder_stade.pkl")
# En production
encoder = joblib.load("ordinal_encoder_stade.pkl")
new_data_encoded = encoder.transform(new_data[["stade_de_developpement"]])
Pipeline Scikit-Learn
Pour éviter les oublis et garantir la reproductibilité, intégrer les encodeurs dans un pipeline :
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestRegressor
pipeline = Pipeline([
("encoder", OrdinalEncoder(mapping=mapping)),
("model", RandomForestRegressor())
])
pipeline.fit(X_train, y_train)
# L'encodage est automatiquement appliqué à chaque prédiction
predictions = pipeline.predict(X_test)
Cas Particulier : Pourquoi espece N'Est Pas Appliquée à Notre DataFrame ?
Dans notre notebook, l'encodage de la variable espece est appliqué sur le dataset complet (data), mais pas intégré au DataFrame filtré df. Pourquoi ?
Raison pédagogique : df ne contient que des Platanes (une seule espèce). Encoder une seule catégorie créerait une colonne constante (toujours la même valeur), ce qui est :
- Inutile pour le modèle : Aucune variance, aucune information discriminante
- Redondant : Le modèle apprendrait que cette colonne n'apporte rien
C'est une démonstration technique de la méthode Binary Encoding sur le dataset complet (plusieurs espèces), mais pas une étape réelle de preprocessing pour notre sous-ensemble.
Dans un projet réel avec plusieurs espèces dans df, il faudrait absolument appliquer l'encodage et l'intégrer au DataFrame.
Conclusion : L'Art du Preprocessing Catégoriel
Le choix de la technique d'encodage catégoriel n'est pas anodin. Il impacte directement :
- La performance du modèle : Un mauvais encodage (hiérarchie artificielle) introduit du biais
- Le temps de calcul : La dimensionnalité excessive ralentit l'entraînement
- L'interprétabilité : Comprendre la signification des coefficients appris
Les trois principes directeurs sont :
- Respecter la nature de la variable : Binaire, ordinale ou nominale ?
- Économiser les dimensions : Préférer Binary Encoding à One-Hot pour la haute cardinalité
- Vérifier et valider : Toujours inspecter les distributions avant/après encodage
Ce guide, basé sur un dataset réel de 42 588 arbres parisiens, fournit un framework décisionnel réutilisable pour tout projet de Data Science nécessitant le preprocessing de variables catégorielles.
Ressources Complémentaires
Bibliothèques Utilisées
- Category Encoders : Bibliothèque complète d'encodeurs (Ordinal, Binary, Target, etc.)
- Pandas : Manipulation de DataFrames
- NumPy : Opérations numériques et gestion des valeurs manquantes
Techniques Avancées Non Couvertes
- Target Encoding : Encoder une catégorie par la moyenne de la variable cible (risque de fuite de données)
- Frequency Encoding : Remplacer chaque catégorie par sa fréquence d'apparition
- Embeddings : Représentation vectorielle apprise (utilisée en Deep Learning)
Dataset Source
Le dataset des arbres de Paris est fourni par OpenClassrooms dans le cadre du cours "Initiez-vous au Machine Learning". Il contient plus de 200 000 observations avec des métadonnées sur l'espèce, la localisation, les dimensions et l'état sanitaire des arbres.