Un encodage à chaud des catégories fréquentes

Nous avons appris dans les sections précédentes que la haute cardinalité et les étiquettes rares peuvent faire que certaines catégories n’apparaissent que dans le jeu d’entrainement, entraînant ainsi un sur-ajustement, ou seulement dans le jeu de test, et que nos modèles ne sauraient alors pas comment noter ces observations.

Nous avons également appris précedemment, que si les variables catégorielles contiennent plusieurs étiquettes, alors en les recodant avec des variables fictives, nous élargirons considérablement l’espace des caractéristiques.

Pour éviter ces complications, nous pouvons créer des variables factices uniquement pour les catégories les plus fréquentes

Cette procédure est également appelée “encodage à chaud des catégories fréquentes”.

En fait, dans la solution gagnante de la coupe KDD 2009 : “Winning the KDD Cup Orange Challenge with Ensemble Selection”, les auteurs limitent un encodage à chaud aux 10 étiquettes les plus fréquentes de la variable. Cela signifie qu’ils ne créeraient qu’une seule variable binaire pour chacun des 10 labels les plus fréquents.

OHE des catégories les plus fréquentes ou les plus élevées équivaut à regrouper toutes les catégories restantes sous une nouvelle catégorie. Nous verrons plus en détail comment regrouper les valeurs rares dans une nouvelle catégorie dans un notebook.

Avantages de l’OHE des catégories supérieures

  • Simple à mettre en œuvre
  • Ne nécessite pas d’heures d’exploration des variables
  • N’élargit pas massivement l’espace de présentation
  • Convient aux modèles linéaires

Limitations

  • N’ajoute aucune information qui pourrait rendre la variable plus prédictive
  • Ne conserve pas les informations des étiquettes ignorées

Souvent, les variables catégorielles montrent quelques catégories dominantes tandis que les autres étiquettes n’ajoutent que peu d’informations. Par conséquent, l’OHE des catégories dominantes est une technique simple et utile.

Note

Le nombre de variables supérieures est fixé arbitrairement. Dans le cadre du concours KDD, les auteurs en ont sélectionné 10, mais il aurait pu y en avoir 15 ou 5 également. Ce nombre peut être choisi arbitrairement ou être dérivé de l’exploration des données.

Dans cette démo :

Nous allons voir comment effectuer un encodage à chaud avec :

  • Feature-Engine
import numpy as np
import pandas as pd

# to split the datasets
from sklearn.model_selection import train_test_split

# for one hot encoding with feature-engine
from feature_engine.categorical_encoders import OneHotCategoricalEncoder

data = pd.read_csv('../exportfeature.csv',usecols=['place', 'M1', 'M2', 'Hippodrome','idJockey'],sep=";",encoding='ANSI')


data.head()
# examinons le nombre d'étiquettes de chaque variable

for col in data.columns:
    print(col, ': ', len(data[col].unique()), ' labels')
place :  2  labels
M1 :  67  labels
M2 :  71  labels
Hippodrome :  199  labels
# explorons les catégories uniques
data['M1'].unique()
array(['3a', '1a', 'Da', '8a', '2a', '9a', '4a', '5a', '10a', 'Aa', 'Dm',
       '13a', '7a', '6m', '11a', '6a', '2Da', '0a', '1Disqa', '10m', '3m',
       '3Da', '14a', '5m', '4Dista', '1m', '1Dista', '2Dm', '4Da', '15a',
       '1Da', '10Dista', '3Dista', '5Da', '4m', '6Da', '12a', '0m', '2m',
       '7Da', '8m', '2Dista', 'Rpa', '9m', '7m', '4Disqa', 'Dista',
       '7Dista', '4Distm', '2Dpga', '6Dista', '5Dista', '1Dpga', '1Dm',
       'Am', 'Ta', '16a', '17a', '8Dista', '2Disqa', 'Dpga', '12m', '3Dm',
       '11m', '3Dpga', '9Da', '8Dm'], dtype=object)
data['M2'].unique()
array(['3m', '9Da', 'Da', '7a', '6a', '5a', '2a', '1a', '10a', '3a', '4m',
       '0a', '4a', '8a', '9a', '2m', '14a', 'Aa', '5Da', '5m', 'Dm',
       '7Dista', '12a', '6m', '2Da', '11a', '7m', '15a', '9m', '4Dm',
       '4Dista', '8m', '1Da', '16a', 'Am', 'Dista', '3Da', '3Dista', '1m',
       '1Dista', '0m', '8Dpga', '13a', '1Disqa', '3Disqa', '5Dista',
       '1Dm', '4Da', '3Distm', '10m', '6Da', 'Ta', '2Dpga', '7Da', '13m',
       '2Dista', '1Dpga', '5Dm', '12m', '8Da', '8Dista', '11m', '6Dista',
       '9Dista', '17a', '12Dist.a', '14m', '5Dpga', '2Dm', 'Dpga', '8Dm'],
      dtype=object)
data['Hippodrome'].unique()
array(['Vincennes', 'Wolvega', 'Argentan', 'Toulouse', 'Cagnes-sur-Mer',
       'Mons', 'Cagnes', 'Cordemais', 'Marseille', 'Geelong',
       'Marseille-Vivaux', 'Gelsenkirchen', 'Son Pardo  Majorque',
       'Nantes', 'Lyon', 'Lyon-La Soie', 'Son Pardo', 'Agen',
       'Vienne Krieau', 'Saint', 'Saint-Galmier', 'Bordeaux',
       'Bordeaux  Le Bouscat', 'Mauquenchy', 'Châteaubriant', 'Kuurne',
       'Angers', 'Grenade-sur-Garonne', 'Graignes', 'Laval', 'Vire',
       'Enghien', 'Le Croisé-Laroche', 'Berlin-Mariendorf', 'Avenches',
       'Le Croisé', 'Pontchâteau', 'Chartres', 'Caen', 'Amiens',
       'Munich-Daglfing', 'Machecoul', 'Lyon-Parilly', 'Lisieux',
       'Hyères', 'Fougères', 'Reims', 'Castillonnès', 'Cherbourg',
       'Marseille-Borély', 'Maure', 'Maure-de-Bretagne',
       'Ebreichsdorf (Magna Racino)', 'La Capelle', 'Beaumont', 'Tours',
       'Beaumont-de-Lomagne', 'Tours-Chambray', 'Nort-sur-Erdre',
       'Le Mans', 'Tongres', 'Chatillon', 'Strasbourg',
       'Chatillon-sur-Chalaronne', 'Challans', 'Meslay',
       'Meslay-du-Maine', 'Cavaillon', 'Saint-Malo', 'Rambouillet',
       'Oraison', 'Aby', 'Alençon', 'Cholet', 'Montluçon', 'Nîmes',
       'Vitré', 'Avignon', 'Charlottenlund', 'Paray-le-Monial',
       'Montauban', 'Vichy', 'Laon', 'Villeneuve-sur-Lot', 'Solvalla',
       'Vannes', 'Nancy', 'La Roche', 'La Roche-Posay', 'Frauenfeld',
       'Lignières', 'Saint-Brieuc', 'Chatelaillon',
       'Chatelaillon-La Rochelle', 'Feurs', 'Ostersund', 'Erbray',
       'Marsa', 'Bjerke', 'Kouvola', 'Boden', 'Arras', 'Segré',
       'Duindigt', 'La Gacilly', 'Agon-Coutainville', 'Sablé-sur-Sarthe',
       'Cabourg', 'Pornichet', 'Biarritz', 'Jarlsberg', 'Eauze', 'Arjang',
       'Royan-la Palmyre', 'Mikkeli', 'Clairefontaine',
       'Les Sables-d&#039Olonne', 'Bernay', 'L&#039Isle-sur-La Sorgue',
       'Jägersro', 'Le Touquet', 'Aix-les-Bains', 'Saint-Jean-de-Monts',
       'Carpentras', 'Bréhal', 'Vittel', 'Auch', 'Dieppe',
       'Langon-Libourne', 'Villeréal', 'Zonza', 'Le Mont-Saint-Michel',
       'Craon', 'Montier-en-Der', 'Divonne-les-Bains', 'Bergsaker',
       'Carentan', 'Waregem', 'Ajaccio', 'Baden', 'Ecommoy', 'Angoulême',
       'Salon-de-Provence', 'Castera-Verduzan', 'Eskilstuna', 'Ballarat',
       'Melton', 'Loudéac', 'Vermo', 'Hambourg Bahrenfeld', 'Niort',
       'Dundalk', 'Saint-Omer', 'Montluçon-Néris-les-Bains', 'Straubing',
       'Ostende', 'Dielsdorf', 'Landivisiau', 'Prunelli-di-Fiumorbo',
       'Dinslaken', 'Yonkers-New York', 'Kilmore', 'Saint-Moritz',
       'Hambourg Horn', '(Q+ du mardi 25', 'Moenchengladbach', '<br',
       'lundi 02', '(R1C5) lundi 14', 'Singapour', 'Farjestad',
       'Halmstad', 'Axevalla', 'Gavle', 'Hagmyren', 'Mantorp', 'Orebro',
       'Bollnas', 'Romme', 'Umaker', 'Dannero', 'Rattvik', 'Vaggeryd',
       'Aalborg', 'Leangen', 'Biri', 'Skive', 'Bergen', 'Momarken',
       'Amal', 'Forus', 'Odense', 'Harstad', 'Visby', 'Skelleftea',
       'Kalmar', 'Meadowlands', '(Gr.II) jeudi 12', '(R1C1)  Vendredi 04'],
      dtype=object)

Encodage important

Il est important de sélectionner les catégories les plus importantes ou les plus fréquentes sur la base des données relatives aux trains. Ensuite, nous utiliserons ces catégories supérieures pour coder les variables dans les données de test

# séparons en ensemble de formation et de test

X_train, X_test, y_train, y_test = train_test_split(
    data[['M1', 'M2', 'Hippodrome','idJockey']],  
    data['place'],  
    test_size=0.3,  
    random_state=0)

X_train.shape, X_test.shape

((14352, 4), (6152, 4))

# Examinons d'abord comment OHE élargit l'espace des fonctionnalités

pd.get_dummies(X_train, drop_first=True).shape

(14352, 1994)

Des 3 variables catégorielles initiales, nous arrivons à 1994 variables.

Ces chiffres ne sont pas encore énormes et, dans la pratique, nous pourrions travailler avec eux relativement facilement. Toutefois, dans les ensembles de données de la vie réelle, les variables catégorielles peuvent être très cardinales, et avec l’OHE, nous pouvons nous retrouver avec des ensembles de données comportant des milliers de colonnes.

 

Un encodage à chaud des catégories supérieures avec Feature-Engine

Avantages

  • rapide
  • crée le même nombre de caractéristiques dans le train et le banc d’essai

Limitations

  • Aucune à ma connaissance

**Note

Si la variable argument est laissée à None, alors le codeur identifiera automatiquement toutes les variables catégorielles. N’est-ce pas adorable ?

Le codeur ne codera pas les variables numériques. Donc, si certaines de vos variables numériques sont en fait des catégories, vous devrez les reformuler en tant qu’objet avant d’utiliser l’encodeur. C’est le cas pour notre catégorie idJockey

X_train['idJockey'] = X_train['idJockey'].astype(object)
X_test['idJockey'] = X_test['idJockey'].astype(object)

ohe_enc = OneHotCategoricalEncoder(
    top_categories=10,  # Par défaut on prend 10 mais vous pouvez l'augmenter
    # Nous pouvons sélectionner spécifiquement les variables que l'on veut utiliser
    variables=['M1', 'M2', 'Hippodrome','idJockey'],
    drop_last=False)

ohe_enc.fit(X_train)

# dans l'encodeur, nous pouvons observer chacune des catégories supérieures
# sélectionné pour chacune des variables
ohe_enc.encoder_dict_
{'M1': ['1a', '2a', 'Da', '3a', '4a', '5a', '6a', '7a', '8a', '9a'],
 'M2': ['1a', 'Da', '2a', '3a', '4a', '5a', '6a', '7a', '8a', '9a'],
 'Hippodrome': ['Vincennes',
  'Cagnes-sur-Mer',
  'Enghien',
  'Mons',
  'Avenches',
  'Cabourg',
  'Vichy',
  'Laval',
  'Caen',
  'Mauquenchy'],
 'idJockey': [2, 34, 6, 4, 28, 360, 1931, 143, 3, 22]}
# voici la liste des variables que le codeur va transformer

ohe_enc.variables

['M1', 'M2', 'Hippodrome', 'idJockey']
X_train = ohe_enc.transform(X_train)
X_test = ohe_enc.transform(X_test)

# explorons le résultat
X_train.head()
	M1_1a	M1_2a	M1_Da	M1_3a	M1_4a	M1_5a	M1_6a	M1_7a	M1_8a	M1_9a	...	idJockey_2	idJockey_34	idJockey_6	idJockey_4	idJockey_28	idJockey_360	idJockey_1931	idJockey_143	idJockey_3	idJockey_22
2540	0	0	0	0	0	1	0	0	0	0	...	0	0	0	0	0	0	0	0	0	0
19008	1	0	0	0	0	0	0	0	0	0	...	0	0	0	1	0	0	0	0	0	0
7624	0	0	0	0	0	0	0	1	0	0	...	0	0	0	0	0	0	0	0	0	0
2575	0	1	0	0	0	0	0	0	0	0	...	0	0	0	0	0	0	0	0	0	0
852	0	0	0	0	0	0	1	0	0	0	...	0	0	0	0	0	0	0	0	0	0
5 rows × 40 columns

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.