3 - Préparer et analyser les données

Nettoyer les données

(Data cleaning ou data cleansing en anglais)

Après une première phase de nettoyage “en superficie” — le formattage — des données, l’analyse des données (exploratory data analysis [EDA]) consiste à creuser plus en profondeur et à s’assurer que les données utilisées sont correctes pour construire le modèle.

Données manquantes

Vérifier la présence de données manquantes. Les valeurs manquantes peuvent fausser les outils statistiques tels que la moyenne, la variance et le pourcentage, ce qui peut mener à des conclusions trompeuses et expliquer un modèle peu performant.

Données catégoriques

  1. Identifier toutes les caractéristiques catégoriques comme telles. Une colonne contenant des valeurs 1,2,3,etc sera a priori considérée comme numérique. Si les numéros sont des labels (ex: siège 1,2,3), alors il faut caster cette caractéristique — puisque ça n’aurait pas de sens de calculer la moyenne de valeurs catégoriques.

  2. Certains algorithmes ne peuvent gérer que des valeurs numériques. En cas de valeurs catégoriques, on peut:

    • Supprimer la caractéristique.
      Cette approche ne va être appropriée que si la caractéristique ne contient pas d’informations utiles.

    • Encoder les labels (label encoding)
      Assigner un entier à chaque valeur différente.
      Cette approche est appropriée s’il existe un ordre logique entre les différentes catégories. Par exemple:
      non (0), oui (1)
      jamais (0), rarement (1), la plupart du temps (2), tous les jours (3)

      python
      from sklearn.preprocessing import LabelEncoder
      label_encoder = LabelEncoder()
      labels = {}
      
      for col in object_cols:
          X_train[col] = label_encoder.fit_transform(X_train[col])
          X_valid[col] = label_encoder.transform(X_valid[col])
          labels[col]  = list(encoder.classes_)
      
      values        = df.sex.astype('category')
      df['sex']     = values.cat.codes
      labels['sex'] = values.cat.categories
      
    • Encoder un à un (one-hot encoding)
      Créer un nouvelle colonne pour chaque valeur possible (caractéristiques couramment appelées 0/1 dummy variables).
      Cette approche est appropriée s’il n’existe qu’un nombre limité de valeurs (<15).

      python
       from sklearn.preprocessing import OneHotEncoder
      
       # Apply one-hot encoder to each column with categorical data
       OH_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
       OH_cols_train = pd.DataFrame(OH_encoder.fit_transform(X_train[object_cols]))
       OH_cols_valid = pd.DataFrame(OH_encoder.transform(X_valid[object_cols]))
      
       # One-hot encoding removed index; put it back
       OH_cols_train.index = X_train.index
       OH_cols_valid.index = X_valid.index
      
       # Remove categorical columns (will replace with one-hot encoding)
       num_X_train = X_train.drop(object_cols, axis=1)
       num_X_valid = X_valid.drop(object_cols, axis=1)
      
       # Add one-hot encoded columns to numerical features
       OH_X_train = pd.concat([num_X_train, OH_cols_train], axis=1)
       OH_X_valid = pd.concat([num_X_valid, OH_cols_valid], axis=1)
       
       df = df.join(pd.get_dummies(df.region, prefix='region')).drop('region', axis=1)
       df.head()
       
    • Encoder le décompte (count encoding)
      Remplacer chaque valeur catégorique par le nombre de fois qu’elle apparaît dans les données. Si la valeur “GB” apparaît 10 fois dans la colonne “pays”, “GB” sera remplacée par 10.

      python
       import category_encoders as ce
       cat_features = ['category', 'currency', 'country']
      
       # Create the encoder
       count_enc = ce.CountEncoder()
      
       # Transform the features, rename the columns with the _count suffix, and join to dataframe
       count_encoded = count_enc.fit_transform(ks[cat_features])
       data = data.join(count_encoded.add_suffix("_count"))
       
    • Encoder la cible (target encoding)
      Remplacer chaque valeur catégorique par la moyenne de la valeur prédite pour cette catégorie. Par exemple, si la valeur prédite est le salaire, le valeur “GB” de la colonne “pays” sera remplacée par salaire moyen du pays.

      Il est nécessaire d’utiliser les mêmes valeurs pour l’entrainement et pour les tests / prédictions. Recalculer le salaire moyen avec les données de test constituerait une fuite des données prédites (target leakage).

      python
       # Create the encoder
       target_enc = ce.TargetEncoder(cols=cat_features)
       target_enc.fit(train[cat_features], train['outcome'])
      
       # Transform the features, rename the columns with _target suffix, and join to dataframe
       train_TE = train.join(target_enc.transform(train[cat_features]).add_suffix('_target'))
       valid_TE = valid.join(target_enc.transform(valid[cat_features]).add_suffix('_target'))
       
    • Encoder la cible avec augmentation (CatBoost encoding)
      Similaire à l’exemple précédent sauf que pour chaque ligne, la valeur est calculée uniquement à partir des lignes avant la ligne en cours.

      python
       # Create the encoder
       target_enc = ce.CatBoostEncoder(cols=cat_features)
       target_enc.fit(train[cat_features], train['outcome'])
      
       # Transform the features, rename columns with _cb suffix, and join to dataframe
       train_CBE = train.join(target_enc.transform(train[cat_features]).add_suffix('_cb'))
       valid_CBE = valid.join(target_enc.transform(valid[cat_features]).add_suffix('_cb'))
       

Date et heure

Typiquement, on récupère les données au format timestamp et on sépare les informations dans de nouvelles colonnes (années, mois, jour, heure, minute et seconde) — suivant la précision souhaitée.

python
data['day']    = data['click_time'].dt.day.astype('uint8')
data['hour']   = data['click_time'].dt.hour.astype('uint8')
data['minute'] = data['click_time'].dt.minute.astype('uint8')
data['second'] = data['click_time'].dt.second.astype('uint8')

Données extrêmes

Corrélations


Séparer les données

(Split data en anglais)

Train/test

Test/validation

On prend un peu d’avance, mais après avoir entraîné le modèle on devra également optimiser ses performances (ex: changer les hyperparamètres). Pour ne pas optimiser et choisir un modèle en se basant uniquement sur le test set (et potentiellement avoir un modèle plus performant sur cet ensemble de données mais par hasard), on sépare les données non plus en 2 mais en 3 ensembles:


Mettre à échelle

(Feature scaling en anglais)

Il est parfois nécessaire de mettre les données à l’échelle pour que l’algorithme utilisé fonctionne correctement.
Une bonne pratique est de tester le modèle avec et sans mise à échelle, et comparer leurs performances.

Pour aller plus loin: Data Cleaning in Python: the Ultimate Guide (2020)