ML · Chapitre 4
Machine learning 4 — Préparation des données
Les chapitres précédents ont introduit les principaux modèles supervisés : régression linéaire, voisins, naïf bayésien, arbres et méthodes d'ensemble. Tous ces algorithmes ont été appliqués à des jeux de données déjà bien rangés, presque parfaits : pas de valeurs manquantes, des variables exclusivement numériques, des classes équilibrées. La réalité du terrain est nettement moins confortable. La plupart du temps, un projet de machine learning commence par un jeu de données brut, hétérogène, partiellement incomplet, dans lequel les classes sont déséquilibrées et où les variables mélangent grandeurs numériques et catégories textuelles.
Ce chapitre est consacré à cette étape préalable, souvent désignée par l'expression anglaise data wrangling. Nous y verrons comment détecter et traiter les valeurs manquantes, comment encoder les variables catégorielles, comment normaliser ou standardiser les variables numériques, et comment gérer les classes déséquilibrées. Nous nous appuierons sur sept jeux de données — Palmer Penguins, Titanic, Mushrooms, Student Performance, Credit Card Fraud, Telecom Churn et Adult Census — pour illustrer chaque problématique dans son contexte réel.
L'enjeu, au-delà de la maîtrise technique, est d'introduire un mode de pensée : avant de choisir un modèle, on regarde ses données.
Le dataset Palmer Penguins
Le dataset Palmer Penguins est un grand classique pédagogique, souvent présenté comme une alternative moderne au célèbre Iris. Il contient des mesures morphologiques de manchots observés dans l'archipel Palmer, en Antarctique, et propose un mélange équilibré de variables numériques et catégorielles. L'objectif est de prédire l'espèce (species) — Adelie, Chinstrap ou Gentoo — à partir des caractéristiques physiques de l'animal et de son environnement d'observation. On est donc face à un problème de classification multiclasse à trois classes.
La cible species est complétée par quatre variables numériques (bill_length_mm, bill_depth_mm, flipper_length_mm et body_mass_g) et deux variables catégorielles (island avec trois modalités et sex avec deux modalités). Le dataset comporte volontairement quelques valeurs manquantes, ce qui en fait un excellent support pédagogique pour aborder les stratégies d'imputation.
df = pd.read_csv('penguins.csv') df.head()
Visualisation exploratoire avec coloration par classe
Avant tout traitement, il est essentiel de visualiser le jeu de données. Dans un problème de classification, la question centrale est celle de la séparabilité des classes : existe-t-il des variables, ou des combinaisons de variables, qui distinguent nettement les espèces ? Seaborn fournit le paramètre hue pour colorer les observations selon une variable catégorielle, ce qui permet de répondre visuellement à cette question.
import seaborn as sns import matplotlib.pyplot as plt sns.set_style("whitegrid") sns.histplot(data=df, x="bill_length_mm", hue="species", bins=30, kde=True, element="step") plt.title("Distribution de la longueur du bec selon l'espèce") plt.show()
L'histogramme par espèce révèle la distribution de la longueur du bec pour chaque classe et permet de repérer immédiatement les zones où les classes se chevauchent ou se distinguent. Le boxplot offre une lecture plus synthétique en comparant médianes et quartiles :
sns.boxplot(data=df, x="species", y="flipper_length_mm") plt.title("Longueur des nageoires selon l'espèce") plt.show()
Le violin plot combine boxplot et estimation de densité, et donne une image plus riche de la forme interne des distributions :
sns.violinplot(data=df, x="species", y="body_mass_g", inner="quartile") plt.show()
Le scatter plot est sans doute l'outil le plus parlant pour évaluer la séparabilité : en croisant deux variables, on voit immédiatement si les classes occupent des régions distinctes du plan.
sns.scatterplot(data=df, x="bill_length_mm", y="bill_depth_mm", hue="species") plt.show()
Sur Penguins, ce graphique révèle des nuages bien distincts : longueur et profondeur du bec suffisent à séparer la plupart des observations. Pour aller plus loin, le pairplot affiche toutes les paires de variables numériques d'un coup :
sns.pairplot(df, vars=["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"], hue="species", diag_kind="kde")
Pour une vue tridimensionnelle interactive, on peut utiliser Plotly Express :
fig = px.scatter_3d(df, x="bill_length_mm", y="bill_depth_mm", z="flipper_length_mm", color="species", opacity=0.8) fig.update_traces(marker=dict(size=5)) fig.show()
L'interactivité permet de tourner le graphique, zoomer et inspecter chaque point. C'est sur ce type de visualisation que la séparabilité tridimensionnelle des espèces apparaît le plus nettement.
Traitement des valeurs manquantes
Les valeurs manquantes sont l'un des problèmes les plus courants dans les jeux de données réels. Une mesure n'a pas pu être effectuée, un capteur a renvoyé une erreur, un répondant n'a pas souhaité communiquer une information : autant de raisons qui produisent des cellules vides dans la table. La plupart des algorithmes de scikit-learn ne savent pas traiter directement les valeurs manquantes ; il faut donc les détecter, les comprendre, puis choisir une stratégie pour les traiter.
Identifier les valeurs manquantes
La première étape consiste à quantifier le problème. La méthode isna() de pandas renvoie un DataFrame booléen de même taille, où True indique une valeur manquante. En sommant sur les lignes, on obtient le nombre de valeurs manquantes par colonne :
df.isna().sum() df.isna().any(axis=1).sum()
La seconde ligne renvoie le nombre de lignes contenant au moins une valeur manquante. C'est cette quantité qui détermine ce que coûterait une suppression pure et simple. Sur Penguins, les colonnes typiquement concernées sont les quatre mesures morphologiques et la variable sex.
Stratégie 1 — la suppression (dropna)
La solution la plus directe consiste à supprimer toutes les lignes contenant au moins une valeur manquante :
df_drop = df.dropna()
Cette méthode a le mérite de la simplicité et n'introduit aucune hypothèse sur les données. Mais elle a un coût : on perd de l'information, et si les valeurs manquantes ne sont pas distribuées aléatoirement, on introduit un biais. Elle reste acceptable lorsque peu de lignes sont concernées et que le dataset reste suffisamment grand après suppression.
Stratégie 2 — l'imputation simple (fillna)
L'imputation consiste à remplacer chaque valeur manquante par une valeur plausible. Pour une variable numérique, on choisit typiquement la moyenne ou la médiane ; pour une variable catégorielle, la modalité la plus fréquente :
df["body_mass_g"] = df["body_mass_g"].fillna(df["body_mass_g"].median()) df["sex"] = df["sex"].fillna(df["sex"].mode()[0])
L'imputation par la médiane est généralement préférable à la moyenne : elle est moins sensible aux valeurs extrêmes. Cette méthode est rapide et facile à expliquer, mais elle réduit artificiellement la variance des variables imputées et ne tient pas compte des relations entre variables.
Stratégie 3 — l'imputation par les voisins (KNNImputer)
Une approche plus sophistiquée consiste à estimer chaque valeur manquante à partir des observations qui lui ressemblent. C'est le principe du KNNImputer : pour une ligne incomplète, on cherche ses k plus proches voisines dans l'espace des variables disponibles, et la valeur manquante est remplacée par une moyenne pondérée des voisines.
from sklearn.impute import KNNImputer imputer = KNNImputer(n_neighbors=5) X_imputed = imputer.fit_transform(X)
Cette méthode exploite les relations entre variables et fournit souvent une imputation plus réaliste. Elle est cependant plus coûteuse en calcul et sensible à l'échelle des variables : il est recommandé de standardiser les données numériques avant d'appliquer un KNNImputer.
Quelle stratégie choisir ? Sur Penguins, la suppression est raisonnable : seules quelques lignes sont concernées, et le dataset reste largement suffisant pour l'apprentissage. Sur des datasets plus grands ou plus précieux, on préférera l'imputation par la médiane ou par les voisins.
Conversion des variables non numériques
La plupart des algorithmes de machine learning classiques attendent des matrices numériques en entrée. Les variables textuelles, comme species, island ou sex dans Penguins, doivent donc être converties avant l'apprentissage. Le bon réflexe est d'inspecter d'abord les types de chaque colonne :
df.dtypes
On distingue typiquement les colonnes numériques (int, float) des colonnes textuelles (object).
Conversion par dictionnaire (map)
Lorsqu'une variable ne prend qu'un petit nombre de modalités bien identifiées, le plus simple est d'utiliser un dictionnaire de correspondance :
df["sex_num"] = df["sex"].map({"male": 1, "female": 0})
Cette méthode est adaptée aux variables binaires ou ordinales pour lesquelles un ordre naturel existe. Elle est en revanche inappropriée aux variables nominales à plus de deux modalités, car elle imposerait un ordre artificiel.
Encodage one-hot (get_dummies)
L'encodage one-hot, ou encodage binaire, consiste à créer une colonne par modalité, prenant les valeurs 0 ou 1. Pour la variable island à trois modalités, on obtient trois colonnes :
df_encoded = pd.get_dummies(df, columns=["island"], drop_first=False)
Le résultat contient les colonnes island_Biscoe, island_Dream et island_Torgersen, exactement une étant à 1 par ligne. Cette représentation a l'énorme avantage de ne supposer aucun ordre entre les modalités, ce qui est exactement ce qu'on veut pour une variable nominale.
Pour les arbres de décision et leurs variantes (forêts, gradient boosting), l'encodage binaire est particulièrement bien adapté : chaque test au sein d'un nœud devient un test simple et interprétable de la forme
island_Biscoe == 1. À l'inverse, un encodage ordinal naïf — Biscoe = 0, Dream = 1, Torgersen = 2 — imposerait un ordre artificiel sans signification, susceptible de tromper le modèle.
Le dataset Titanic
Le dataset Titanic rassemble des informations sur les passagers du paquebot et vise à prédire leur survie (Survived, 0 ou 1) à partir de variables variées : sexe, âge, classe du billet, prix payé, port d'embarquement, et nombre de membres de la famille à bord (SibSp, Parch). C'est un cas typique d'application combinant variables numériques (Age, Fare), variables catégorielles à peu de modalités (Sex, Embarked, Pclass) et variables catégorielles à très grand nombre de modalités (Cabin, Ticket).
df = pd.read_csv("titanic.csv") df.head()
Le traitement est désormais classique : repérer les valeurs manquantes (notamment Age et Cabin), choisir une stratégie d'imputation, encoder les variables catégorielles, puis entraîner un modèle.
Le dataset Mushrooms
Le dataset Mushrooms est un cas extrême : il est composé presque exclusivement de variables catégorielles. Chaque champignon est décrit par une vingtaine de caractéristiques nominales — forme du chapeau, couleur, odeur, type de lamelles, habitat — et la cible class indique s'il est comestible (e) ou toxique (p).
Lorsque le nombre de colonnes est important, l'affichage par défaut de pandas en cache certaines. Pour les voir toutes :
pd.set_option("display.max_columns", None) df.head()
Compter les valeurs distinctes
Avant d'encoder une variable catégorielle, il est utile de connaître le nombre de modalités qu'elle peut prendre. Un encodage one-hot d'une variable à 50 modalités produit 50 nouvelles colonnes, ce qui peut être problématique. Plusieurs méthodes sont disponibles :
np.unique(df["odor"]) df["odor"].unique() df["odor"].value_counts() df["odor"].nunique()
np.unique et pandas.unique renvoient les modalités distinctes ; value_counts ajoute leur fréquence ; nunique se contente du nombre total. Pour analyser tout le dataset d'un coup :
for col in df.columns: print(f"{col:25s} : {df[col].nunique()} valeurs distinctes")
Cette boucle révèle parfois des colonnes constantes — une seule modalité, donc aucune information — qu'il convient simplement de supprimer. C'est par exemple le cas de veil-type dans Mushrooms, qui ne prend qu'une valeur.
Premier modèle sur Mushrooms
Une fois les variables encodées, on peut appliquer un modèle d'ensemble, par exemple une forêt aléatoire :
df = pd.read_csv("mushrooms.csv") X = df.drop(columns=['class']) y = df['class'].to_numpy() X = X.drop(columns='veil-type') X = pd.get_dummies(X, columns=X.columns) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2) model = RandomForestClassifier(n_estimators=200, max_depth=5) model.fit(X_train, y_train) y_hat = model.predict(X_test) print("Accuracy =", accuracy_score(y_test, y_hat)) print(classification_report(y_test, y_hat)) print(confusion_matrix(y_test, y_hat))
Sur ce dataset, les performances sont quasi parfaites. Cela invite à une question importante : feriez-vous confiance à un modèle de machine learning pour décider, en pratique, si un champignon est comestible ? Les performances en validation sont excellentes, mais le risque d'erreur — même rare — est ici dramatique. C'est un bon rappel que la qualité statistique d'un modèle ne garantit pas sa pertinence opérationnelle.
CatBoost et la gestion native des variables catégorielles
Dans un dataset comme Mushrooms, l'encodage one-hot crée énormément de colonnes. Une alternative consiste à utiliser CatBoost, un algorithme de gradient boosting qui gère directement les variables catégorielles, sans encodage préalable.
CatBoost utilise des statistiques de cible régularisées : chaque modalité est représentée par une statistique sur la cible (par exemple le taux moyen de comestibilité), calculée selon des ordres aléatoires des données — l'ordered boosting — pour éviter toute fuite d'information. CatBoost s'appuie également sur des arbres symétriques, à profondeur fixe, ce qui améliore la stabilité et réduit le besoin de réglage fin.
from catboost import CatBoostClassifier model = CatBoostClassifier( iterations=500, learning_rate=0.05, depth=6, loss_function="Logloss", verbose=False ) cat_features = X.columns.tolist() model.fit(X_train, y_train, cat_features=cat_features)
L'argument cat_features indique à CatBoost les colonnes à traiter en catégoriel. Le modèle existe également en régression (CatBoostRegressor) avec une logique strictement identique, simplement la loss_function change (par exemple "RMSE").
Le dataset Student Performance et la sélection par type
Le dataset Student Performance rassemble des données sur des élèves de lycées portugais : informations démographiques, contexte scolaire, mode de vie, et la cible G3 qui est la note finale sur 20. C'est un problème de régression mêlant variables numériques (âge, absences, notes intermédiaires) et catégorielles (école, sexe, profession des parents, etc.).
Sélection par dtype
Avec un dataset comportant beaucoup de colonnes hétérogènes, il devient impraticable de lister manuellement les variables catégorielles et numériques. Pandas fournit la méthode select_dtypes qui sélectionne automatiquement les colonnes selon leur type :
cat_features = X.select_dtypes( include=["object", "category", "bool"] ).columns.tolist() num_features = [col for col in X.columns if col not in cat_features]
Cette construction garantit une partition propre : chaque colonne tombe dans l'une ou l'autre catégorie, sans oubli ni doublon. C'est un idiome qu'on retrouvera systématiquement dans les pipelines de production.
Comparer XGBoost et CatBoost
Sur Student Performance, on peut comparer deux approches : CatBoost avec gestion native du catégoriel, ou XGBoost avec encodage one-hot préalable.
from catboost import CatBoostRegressor model = CatBoostRegressor(verbose=False) model.fit(X_train, y_train, cat_features=cat_features) y_hat = model.predict(X_test) print(f"MAE : {mean_absolute_error(y_test, y_hat):.2f}") print(f"RMSE : {np.sqrt(mean_squared_error(y_test, y_hat)):.2f}") print(f"R2 : {r2_score(y_test, y_hat):.3f}")
Avec XGBoost, l'encodage est explicite :
X = pd.get_dummies(X, columns=cat_features) model = XGBRegressor() model.fit(X_train, y_train)
Une remarque méthodologique importante : le dataset contient les variables G1 et G2, qui sont les notes des deux trimestres précédant G3. Elles sont massivement corrélées à la cible et rendent la prédiction triviale. Pour évaluer un modèle dans un cadre réaliste — prédire la note finale en début d'année — il faut les supprimer :
df = df.drop(columns=['G1', 'G2'])
C'est un cas typique de data leakage à interroger dès qu'un modèle obtient des performances suspicieusement excellentes.
Corrélations entre variables numériques
Une corrélation mesure le degré d'association entre deux variables, sur une échelle généralement comprise entre et . Le signe indique le sens (positif ou négatif), la valeur absolue l'intensité. Trois mesures principales sont disponibles :
- la corrélation de Pearson mesure une relation linéaire entre deux variables numériques continues ; elle est sensible aux valeurs extrêmes et suppose une relation approximativement linéaire ;
- la corrélation de Spearman est calculée sur les rangs des valeurs et mesure une relation monotone, pas nécessairement linéaire ; elle est plus robuste aux outliers ;
- la corrélation de Kendall évalue la concordance entre paires d'observations et est souvent utilisée sur des jeux de données plus petits ou bruités.
Pandas fournit la méthode .corr() qui calcule la matrice de corrélation entre toutes les variables numériques :
corr = df.corr() corr_spearman = df.corr(method="spearman") corr_kendall = df.corr(method="kendall")
La visualisation classique est la heatmap :
sns.heatmap(corr, cmap="coolwarm", center=0) plt.show()
Pour aller plus loin, la clustermap combine la heatmap avec un clustering hiérarchique des variables, ce qui regroupe automatiquement les variables similaires en blocs visibles :
sns.clustermap(corr, cmap="coolwarm", center=0) plt.show()
Travailler sur la valeur absolue de la corrélation est souvent utile : l'intensité de la liaison nous intéresse parfois plus que son sens.
Le dataset Credit Card Fraud et le déséquilibre des classes
Le dataset Credit Card Fraud contient des transactions par carte bancaire effectuées sur deux jours par des clients européens. La cible Class vaut 0 pour une transaction légitime et 1 pour une fraude. Les variables explicatives sont Time, Amount, et V1 à V28, ces dernières issues d'une analyse en composantes principales (PCA) appliquée aux variables originales pour des raisons de confidentialité.
Le défi de ce dataset est son déséquilibre extrême : les fraudes représentent environ 0,17 % des transactions. C'est un cas d'école pour comprendre les pièges du déséquilibre des classes et les techniques pour les contourner.
Le piège de l'accuracy
Considérons un modèle qui prédit systématiquement « pas de fraude » :
il classe correctement 99,83 % des transactions ; son accuracy est excellente ; mais il ne détecte aucune fraude. Il est totalement inutile en pratique.
L'accuracy mesure :
Dans un dataset déséquilibré, la classe majoritaire domine ce calcul : les erreurs sur la classe rare ont peu d'impact. Un modèle peut être performant selon l'accuracy tout en échouant complètement sur l'objectif réel. Il faut donc privilégier d'autres métriques : précision, rappel, F1, AUC ROC, ou matrices de confusion détaillées.
Conserver la stratification : stratify
Lors du train_test_split, on peut imposer que la répartition des classes soit la même dans le train et dans le test, grâce au paramètre stratify :
X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y)
Sans cette option, sur un dataset très déséquilibré, le hasard peut placer la quasi-totalité des cas rares dans le train ou dans le test, rendant l'évaluation instable. Attention toutefois : stratify ne rééquilibre pas les classes — il préserve simplement leur proportion.
Sous-échantillonnage et sur-échantillonnage avec imblearn
La librairie imbalanced-learn (imblearn) fournit un ensemble d'outils pour rééquilibrer un dataset déséquilibré. Trois grandes familles existent.
Le sous-échantillonnage réduit la classe majoritaire :
from imblearn.under_sampling import RandomUnderSampler sampler = RandomUnderSampler() X_res, y_res = sampler.fit_resample(X, y)
Le sur-échantillonnage augmente la classe minoritaire, soit en dupliquant des observations existantes (RandomOverSampler), soit en générant de nouveaux points synthétiques par interpolation (SMOTE, ADASYN) :
from imblearn.over_sampling import SMOTE sampler = SMOTE() X_res, y_res = sampler.fit_resample(X, y)
Les méthodes combinées (SMOTEENN, SMOTETomek) appliquent un sur-échantillonnage suivi d'un nettoyage des frontières.
Attention : le rééchantillonnage doit s'effectuer uniquement sur le dataset d'entraînement, jamais sur le test. Sinon, on évalue le modèle sur des données qui ne reflètent pas la distribution réelle du problème.
Pour garantir cette discipline, on intègre le rééchantillonnage dans une pipeline d'imblearn :
from imblearn.pipeline import Pipeline from imblearn.over_sampling import SMOTE model = Pipeline([ ("smote", SMOTE()), ("dtc", DecisionTreeClassifier(random_state=42)) ])
La pipeline applique automatiquement SMOTE au seul jeu d'entraînement lors du fit, et bypasse cette étape lors du predict, ce qui préserve la sémantique du test.
Pondération des classes
Une alternative au rééchantillonnage est la pondération des classes : on ne touche pas aux données, on modifie simplement la fonction de coût pour que les erreurs sur la classe minoritaire soient plus pénalisées. La plupart des algorithmes proposent un mécanisme de pondération.
Pour les arbres et forêts, le paramètre class_weight="balanced" calcule automatiquement les poids inverses des fréquences :
DecisionTreeClassifier(class_weight="balanced") RandomForestClassifier(class_weight="balanced")
On peut aussi spécifier une pondération manuelle, par exemple class_weight={0: 1, 1: 5} pour pénaliser cinq fois plus les erreurs sur la classe positive.
Pour XGBoost, on utilise scale_pos_weight, généralement défini comme le rapport entre nombres d'exemples négatifs et positifs :
XGBClassifier(scale_pos_weight = n_negative / n_positive)
Pour LightGBM, on dispose de is_unbalance=True (automatique) ou de class_weight={0: 1, 1: 5} (explicite, recommandée). Pour CatBoost, c'est auto_class_weights="Balanced" ou class_weights=[1, 5].
| Modèle | Équivalent de class_weight="balanced" |
|---|---|
| Decision Tree | class_weight="balanced" |
| Random Forest | class_weight="balanced" |
| XGBoost | scale_pos_weight |
| LightGBM | class_weight ou is_unbalance |
| CatBoost | auto_class_weights="Balanced" |
La pondération des classes modifie la fonction de coût, pas les données. Elle ne crée ni ne supprime aucune observation. C'est souvent l'approche la plus propre, notamment pour les modèles à base d'arbres.
Ajustement du seuil de décision
Pour un classifieur binaire, la prédiction par défaut applique un seuil de 0,5 sur la probabilité prédite. Sur un problème déséquilibré, ce seuil n'est généralement pas optimal. On peut l'abaisser pour augmenter le rappel sur la classe rare, au prix d'une augmentation des faux positifs :
y_proba = model.predict_proba(X_test)[:, 1] seuil = 0.1 y_hat = (y_proba > seuil).astype(int)
Le bon seuil dépend du coût relatif des erreurs. En détection de fraude, manquer une fraude coûte généralement bien plus cher que de marquer à tort une transaction légitime ; un seuil bas est donc souvent justifié. La courbe ROC et l'AUC permettent de visualiser le compromis :
fpr, tpr, thresholds = roc_curve(y_test, y_proba) auc = roc_auc_score(y_test, y_proba) plt.plot(fpr, tpr); plt.show()
Telecom Churn et Adult Census : récapitulation
Pour clore ce chapitre, deux datasets servent de récapitulation et permettent d'enchaîner toutes les étapes sur des problèmes réalistes.
Le dataset Telecom Churn contient des informations clients (ancienneté, options d'abonnement, consommation, appels au support) et la cible Churn indique si le client a résilié son contrat. Le dataset est modérément déséquilibré, ce qui en fait un bon terrain d'application pour les techniques de pondération :
df = pd.read_csv("churn.csv") X = df.drop(columns=['Churn']) y = df['Churn'] cat_features = X.select_dtypes( include=["object", "category", "bool"]).columns.tolist() X = pd.get_dummies(X, columns=cat_features) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y) model = RandomForestClassifier(class_weight="balanced") model.fit(X_train, y_train)
Avec CatBoost, on évite l'encodage et on ajoute la pondération automatique :
model = CatBoostClassifier(auto_class_weights="Balanced") model.fit(X_train, y_train, cat_features=cat_features, verbose=False)
Le dataset Adult Census Income vise à prédire la tranche de revenu (<=50K ou >50K) à partir d'informations démographiques, éducatives et professionnelles. Il combine variables numériques (âge, heures travaillées, gains en capital) et catégorielles (statut marital, profession, pays d'origine). C'est un cas idéal pour pratiquer le pipeline complet : sélection par dtype, encodage des catégorielles, modèle avec gestion native (CatBoost) ou one-hot (XGBoost).
df['income'] = df['income'].map({'<=50K': 0, '>50K': 1}) X = df.drop(columns=['income']) y = df['income'] cat_features = X.select_dtypes( include=["object", "category", "bool"]).columns.tolist() model = CatBoostClassifier(verbose=False) model.fit(X_train, y_train, cat_features=cat_features)
Exercices
Exercice 1 — Penguins, arbre de décision
- Charger le dataset Penguins.
- Traiter les valeurs manquantes (au choix :
dropna,fillna, ouKNNImputer).- Séparer variables explicatives et cible (
species).- Encoder les variables catégorielles.
- Entraîner un arbre de décision et évaluer la performance sur un jeu de test.
- Afficher l'arbre et interpréter quelques règles.
Exercice 2 — Penguins, forêt aléatoire
- Reprendre le pipeline de l'exercice précédent.
- Remplacer l'arbre par une
RandomForestClassifier.- Comparer les performances et la stabilité par rapport à l'arbre simple.
Exercice 3 — Titanic, prédiction de survie
Construire un pipeline complet : imputation des valeurs manquantes (
Age,Embarked), encodage des variables catégorielles (Sex,Embarked,Pclass), entraînement d'un modèle d'ensemble, et évaluation. Quelles variables se révèlent les plus prédictives ?
Exercice 4 — Mushrooms et CatBoost
- Charger le dataset Mushrooms.
- Identifier les colonnes constantes et les supprimer.
- Entraîner un
CatBoostClassifieren lui passant directement les colonnes catégorielles viacat_features.- Comparer avec une
RandomForestClassifieraprèspd.get_dummies: performance, taille du modèle, temps d'entraînement.
Exercice 5 — Student Performance, XGBoost vs CatBoost
- Charger le dataset.
- Supprimer
G1etG2pour éviter la fuite de cible.- Sélectionner automatiquement les colonnes catégorielles via
select_dtypes.- Entraîner un
CatBoostRegressoret unXGBRegressor(avecpd.get_dummies) ; comparer MAE, RMSE et .
Exercice 6 — Credit Card Fraud, déséquilibre
- Entraîner un arbre de décision sans aucun traitement ; observer l'accuracy et la matrice de confusion.
- Ajouter un
RandomUnderSamplerdans une pipelineimblearn; comparer.- Remplacer par
SMOTE; comparer encore.- Faire varier le seuil de décision (
predict_probapuis comparaison à un seuil) et tracer la courbe ROC.
Exercice 7 — Telecom Churn, pondération des classes
Comparer trois stratégies sur le même dataset : (a) modèle nu, (b) modèle avec
class_weight="balanced", (c)CatBoostClassifieravecauto_class_weights="Balanced". Quelle stratégie offre le meilleur compromis précision/rappel sur la classe positive ?
Exercice 8 — Adult Census, pipeline complet
Construire un pipeline complet sur Adult Census : sélection automatique des colonnes par
dtype, encodage one-hot des catégorielles, comparaison entreXGBClassifier(one-hot) etCatBoostClassifier(gestion native). Que peut-on dire de la stabilité et du temps d'entraînement de chaque approche ?
Pour aller plus loin
- scikit-learn — preprocessing : panorama complet des outils de prétraitement (
StandardScaler,MinMaxScaler,OneHotEncoder,OrdinalEncoder,KBinsDiscretizer). - scikit-learn —
ColumnTransformer: appliquer des transformations différentes à différents sous-ensembles de colonnes au sein d'une même pipeline. - scikit-learn —
Pipeline: chaîner prétraitement et modèle dans un objet unique, indispensable pour la validation croisée et la mise en production. - scikit-learn —
GridSearchCV: recherche exhaustive d'hyperparamètres sur grille, à combiner avec une pipeline pour optimiser proprement le prétraitement et le modèle conjointement. - scikit-learn —
KNNImputeretSimpleImputer: imputation systématique des valeurs manquantes, intégrable dans une pipeline. - imbalanced-learn : sous-échantillonnage, sur-échantillonnage, méthodes combinées et
Pipelinecompatible scikit-learn. - CatBoost — documentation officielle : gradient boosting avec gestion native des variables catégorielles.