Sélection automatique de la meilleure technique d’imputation avec Sklearn

Dans ce carnet, nous allons effectuer une recherche par grille sur les méthodes d’imputation disponibles dans Scikit-learn afin de déterminer quelle technique d’imputation fonctionne le mieux pour cet ensemble de données et le modèle d’apprentissage machine de choix.

Nous formerons également un modèle d’apprentissage machine très simple dans le cadre d’un petit pipeline.

import pandas as pd
import numpy as np

# import classes for imputation
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer

# import extra classes for modelling
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.linear_model import LogisticRegression

np.random.seed(0)

data = pd.read_csv('../exportfeature.csv',parse_dates=['date'],infer_datetime_format=True,dayfirst=True,sep=";",encoding='ANSI')
# On supprime la colonne rapport 
data.drop(['rapport','date','id','rPoids','idOeillere'], axis=1, inplace=True)

data.head()
place	M1	M2	M3	M4	allocation	Hippodrome	nbPartants	idHippodrome	nDistance	nAllocation	idFerrure	idJockey	nbPartants.1	iCote
0	1	3a	3m	Da	3a	90000.0	Vincennes	17	Vincennes	2100	95000	0	18	17	12.0
1	1	3a	9Da	5a	4a	70000.0	Vincennes	18	Vincennes	2700	58000	1	30	18	12.0
2	0	1a	Da	Da	1a	44000.0	Vincennes	12	Vincennes	2850	37000	0	53	10	9.0
3	1	Da	7a	4a	2a	40000.0	Vincennes	11	Vincennes	2200	38000	2	143	13	4.0
4	0	1a	7a	10a	11a	40000.0	Vincennes	16	Vincennes	2700	34000	0	50	17	8.0
# Variable de type catégorie
features_categorical = [c for c in data.columns if data[c].dtypes=='O']

# Variables de type numérique et qui n'est pas place
features_numerical = [c for c in data.columns if data[c].dtypes!='O' and c !='place']

# Variable de type catégorie

data[features_categorical].head()

M1	M2	M3	M4	Hippodrome	idHippodrome
0	3a	3m	Da	3a	Vincennes	Vincennes
1	3a	9Da	5a	4a	Vincennes	Vincennes
2	1a	Da	Da	1a	Vincennes	Vincennes
3	Da	7a	4a	2a	Vincennes	Vincennes
4	1a	7a	10a	11a	Vincennes	Vincennes
# Variable de type numérique

data[features_numerical].head()
	allocation	nbPartants	nDistance	nAllocation	idFerrure	idJockey	nbPartants.1	iCote
0	90000.0	17	2100	95000	0	18	17	12.0
1	70000.0	18	2700	58000	1	30	18	12.0
2	44000.0	12	2850	37000	0	53	10	9.0
3	40000.0	11	2200	38000	2	143	13	4.0
4	40000.0	16	2700	34000	0	50	17	8.0
# Injectons des données absentes
# 1% dans M2
data.loc[data.sample(frac=0.01).index, 'M2'] = np.nan

# 3% dans M4
data.loc[data.sample(frac=0.01).index, 'M2'] = np.nan

# 5% dans allocation
data.loc[data.sample(frac=0.05).index, 'allocation'] = np.nan

# 15% dans idJockey
data.loc[data.sample(frac=0.15).index, 'idJockey'] = np.nan


# 0.06 % dans Hippodrome
data.loc[data.sample(frac=0.06).index, 'Hippodrome'] = np.nan

# 0.65 % dans nbPartants.1
data.loc[data.sample(frac=0.65).index, 'nbPartants.1'] = np.nan
# Création du jeu d'entrainement et de test

X_train, X_test, y_train, y_test = train_test_split(
     data.drop('place', axis=1), 
    data['place'],  
    test_size=0.3,  
    random_state=0)  

X_train.shape, X_test.shape

((14352, 14), (6152, 14))
# Nous créons les pipelines de prétraitement pour les deux
# données numériques et catégorielles

numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())])

categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))])

preprocessor = ColumnTransformer(
    transformers=[
        ('numerical', numeric_transformer, features_numerical),
        ('categorical', categorical_transformer, features_categorical)])

# Notez que pour initialiser le pipeline, je passe n'importe quel argument aux transformateurs.
# Ceux-ci seront modifiés lors de la recherche de réseau ci-dessous.
# Ajouter le classificateur au pipeline de prétraitement.
# Nous avons maintenant un pipeline de prédiction complet.

clf = Pipeline(steps=[('preprocessor', preprocessor),
                      ('classifier', LogisticRegression())])
# maintenant nous créons la grille avec tous les paramètres que nous voudrions tester

param_grid = {
    'preprocessor__numerical__imputer__strategy': ['mean', 'median'],
    'preprocessor__categorical__imputer__strategy': ['most_frequent', 'constant'],
    'classifier__penalty' : ['l1', 'l2'],
    'classifier__C' : np.logspace(-4, 4, 20),
    'classifier__solver' : ['liblinear']
}

grid_search = GridSearchCV(clf, param_grid, cv=5, iid=False, n_jobs=-1, scoring='precision')

# cv=3 est la validation croisée
# no_jobs =-1 indique d'utiliser tous les cpus disponibles
# scoring='r2' indique qu'il faut évaluer en utilisant le r au carré

# pour plus de détails dans la visite des paramètres de la grille :
#https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html

Lors de la définition des paramètres de la grille, c’est ainsi que nous indiquons les paramètres :

préprocesseur__numérique__imputer__stratégie’ : [“moyenne”, “médiane”],

la ligne de code ci-dessus indique que je souhaite tester la moyenne et la médiane dans l’étape d’imputation du processeur numérique.

préprocesseur__catégorie__imputer__stratégie’ : [most_frequent’, ‘constant’]

la ligne de code ci-dessus indique que je souhaite tester la valeur la plus fréquente ou une valeur constante dans l’étape d’imputation du processeur catégorique

classificateur__alpha’ : [0.1, 1.0, 0.5]

la ligne de code ci-dessus indique que je veux tester ces 3 valeurs pour le paramètre alpha du Lasso. Notez que le Lasso est l’étape ‘classificateur’ de notre dernier pipeline

# et maintenant nous nous entraînons sur toutes les combinaisons possibles des paramètres ci-dessus
grid_search.fit(X_train, y_train)

# et nous imprimons le meilleur score sur le train
print(("Meilleur résultat: %.3f"
       % grid_search.score(X_train, y_train)))
       
Meilleur résultat: 0.587       

C’est très loin d’être top comme résultat mais bon… On verra un peu plus loin les différentes mesures pour juger de la qualité d’un modèle

# impression des meilleurs paramétres
grid_search.best_estimator_
Pipeline(steps=[('preprocessor',
                 ColumnTransformer(transformers=[('numerical',
                                                  Pipeline(steps=[('imputer',
                                                                   SimpleImputer()),
                                                                  ('scaler',
                                                                   StandardScaler())]),
                                                  ['allocation', 'nbPartants',
                                                   'nDistance', 'nAllocation',
                                                   'idFerrure', 'idJockey',
                                                   'nbPartants.1', 'iCote']),
                                                 ('categorical',
                                                  Pipeline(steps=[('imputer',
                                                                   SimpleImputer(fill_value='missing',
                                                                                 strategy='most_frequent')),
                                                                  ('onehot',
                                                                   OneHotEncoder(handle_unknown='ignore'))]),
                                                  ['M1', 'M2', 'M3', 'M4',
                                                   'Hippodrome',
                                                   'idHippodrome'])])),
                ('classifier',
                 LogisticRegression(C=0.0018329807108324356, penalty='l1',
                                    solver='liblinear'))])
# impression des meilleurs paramètres
grid_search.best_params_
{'classifier__C': 0.0018329807108324356,
 'classifier__penalty': 'l1',
 'classifier__solver': 'liblinear',
 'preprocessor__categorical__imputer__strategy': 'most_frequent',
 'preprocessor__numerical__imputer__strategy': 'mean'}
# On peut voir les différentes évaluations du modèle
grid_search.cv_results_['params']
[{'classifier__C': 0.0001,
  'classifier__penalty': 'l1',
  'classifier__solver': 'liblinear',
  'preprocessor__categorical__imputer__strategy': 'most_frequent',
  'preprocessor__numerical__imputer__strategy': 'mean'},
 {'classifier__C': 0.0001,
  'classifier__penalty': 'l1',
  'classifier__solver': 'liblinear',
  'preprocessor__categorical__imputer__strategy': 'most_frequent',
  'preprocessor__numerical__imputer__strategy': 'median'},
 {'classifier__C': 0.0001,
  'classifier__penalty': 'l1',
  'classifier__solver': 'liblinear',
  'preprocessor__categorical__imputer__strategy': 'constant',
  'preprocessor__numerical__imputer__strategy': 'mean'},
 {'classifier__C': 0.0001,
  'classifier__penalty': 'l1',
  'classifier__solver': 'liblinear',
  'preprocessor__categorical__imputer__strategy': 'constant',
  'preprocessor__numerical__imputer__strategy': 'median'},....
# et ici les scores pour chacune des combinaisons ci-dessus
grid_search.cv_results_['mean_test_score']
array([0.        , 0.        , 0.        , 0.        , 0.56679338,
       0.5668684 , 0.56695844, 0.56660933, 0.        , 0.        ,
       0.        , 0.        , 0.57151022, 0.57117435, 0.57161545,
       0.57124668, 0.        , 0.        , 0.        , 0.        ,
       0.57909272, 0.57943362, 0.57867718, 0.57871535, 0.61039731,
       0.61039731, 0.61039731, 0.61039731, 0.5852586 , 0.58528091,
       0.58469451, 0.58500554, 0.5751882 , 0.57535101, 0.57562511,
       0.57605211, 0.58884431, 0.58947411, 0.58875039, 0.58898246,
       0.58482377, 0.58467612, 0.58424666, 0.58397768, 0.59030059,
       0.59035226, 0.5900326 , 0.58923194, 0.58951257, 0.58951087,
       0.58964938, 0.58936819, 0.59040801, 0.58965257, 0.5897056 ,
       0.59006782, 0.59174457, 0.59235731, 0.59182597, 0.5920027 ,
       0.58829259, 0.58881793, 0.58863632, 0.58817874, 0.59137442,
       0.59148842, 0.59159803, 0.5919712 , 0.58710895, 0.58720342,
       0.58639316, 0.58684826, 0.58861476, 0.5882917 , 0.58870254,
       0.58857949, 0.58627164, 0.58586007, 0.58639199, 0.58640217,
       0.58486008, 0.58544157, 0.58553539, 0.58554887, 0.58541298,
       0.58430234, 0.58513069, 0.58463359, 0.58484934, 0.58451143,
       0.58420025, 0.58434182, 0.58424058, 0.58469323, 0.58435426,
       0.58460992, 0.58530962, 0.58506749, 0.58505442, 0.58531816,
       0.5848534 , 0.58467124, 0.5851044 , 0.58482227, 0.58532472,
       0.58475155, 0.58527607, 0.58442178, 0.58510619, 0.58475485,
       0.58453388, 0.58479622, 0.58456439, 0.58466107, 0.58456553,
       0.5842657 , 0.58467692, 0.58460191, 0.58480617, 0.58470849,
       0.58446818, 0.58402489, 0.58418728, 0.58400863, 0.58434936,
       0.58431142, 0.58443888, 0.58413899, 0.58423765, 0.58372824,
       0.58405325, 0.58386498, 0.58455694, 0.58409238, 0.58438478,
       0.5841755 , 0.58423386, 0.58391666, 0.58418072, 0.58387934,
       0.58451976, 0.58405258, 0.58435159, 0.5840078 , 0.58412938,
       0.58366507, 0.58403193, 0.58357109, 0.58447239, 0.58400769,
       0.58439593, 0.5840678 , 0.58428171, 0.58385738, 0.58401521,
       0.58386736, 0.58442809, 0.58388154, 0.58433256, 0.58400448])
#  et enfin, vérifions les performances par rapport à l'ensemble des tests
print(("Meilleur modele: %.3f"
       % grid_search.score(X_test, y_test)))
Meilleur modele: 0.582

 

Ce qui est intéressant ici c’est de voir qu’il y a peu de différence entre la phase d’apprentissage et celle de test.

Si vous voulez découvrir comment fonctionne la librairie pandas ou bien l’environnement Jupyter, n’hésitez pas à consulter les cours ci-dessous.

GCH anime

Je télécharge mon guide gratuit

.

Vous recevrez votre guide par email sans aucun engagement de votre part.

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.