Dans le codage du comptage, nous remplaçons les catégories par le comptage des observations qui montrent cette catégorie dans l’ensemble de données. De même, nous pouvons remplacer la catégorie par la fréquence – ou le pourcentage – des observations dans l’ensemble de données. C’est-à-dire que si 10 de nos 100 observations montrent la couleur bleue, nous remplacerons le bleu par 10 si nous effectuons le codage du comptage, ou par 0,1 si nous le remplaçons par la fréquence. Ces techniques permettent de saisir la représentation de chaque étiquette dans un ensemble de données, mais le codage ne permet pas nécessairement de prédire le résultat. Il s’agit toutefois de méthodes de codage très populaires dans les concours Kaggle.
L’hypothèse de cette technique est que le nombre d’observations indiqué par chaque variable est quelque peu révélateur du pouvoir prédictif de la catégorie.
Avantages
- Simple
- N’élargit pas l’espace de présentation
Inconvénients
- Si deux catégories différentes apparaissent le même nombre de fois dans l’ensemble de données, c’est-à-dire qu’elles apparaissent dans le même nombre d’observations, elles seront remplacées par le même nombre : elles peuvent perdre des informations précieuses.
Par exemple, s’il y a 10 observations pour la catégorie bleue et 10 observations pour la catégorie rouge, les deux seront remplacées par 10, et donc, après l’encodage, elles apparaîtront comme étant la même chose.
Dans cette démo :
Nous allons voir comment effectuer le comptage ou l’encodage de fréquence avec :
- Pandas
- Feature-Engine
import numpy as np
import pandas as pd
# to split the datasets
from sklearn.model_selection import train_test_split
# to encode with feature-engine
from feature_engine.categorical_encoders import CountFrequencyCategoricalEncoder
data = pd.read_csv('../exportfeature.csv',usecols=['place', 'M1', 'M2', 'Hippodrome','idJockey'],sep=";",encoding='ANSI')
data.head()
place M1 M2 Hippodrome idJockey
0 1 3a 3m Vincennes 18
1 1 3a 9Da Vincennes 30
2 0 1a Da Vincennes 53
3 1 Da 7a Vincennes 143
4 0 1a 7a Vincennes 50
# 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
idJockey : 1943 labels
Important
Lors de la transformation du comptage des variables catégorielles, il est important de calculer le comptage (ou la fréquence = comptage / total des observations) sur l’ensemble d’entrainement, puis d’utiliser ces nombres pour remplacer les étiquettes dans l’ensemble de test.
data['idJockey'] = data['idJockey'].astype(object)
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))
Comptage et encodage des fréquences avec pandas
# obtenons les comptages pour chacun des labels
# dans la variable Quartier
count_map = X_train['M1'].value_counts().to_dict()
count_map
{'1a': 5015,
'2a': 2250,
'Da': 1744,
'3a': 1415,
'4a': 904,
'5a': 642,
'6a': 514,
'7a': 362,
'8a': 306,
'9a': 258,
'10a': 190,
'0a': 122,
'11a': 102,
'Dm': 72,
'12a': 59,
'13a': 44,
'1m': 32,
'2Da': 28,
'1Da': 25,
'14a': 24,
'3Da': 21,
'3m': 20,
'Aa': 19,
'2m': 17,
'1Dista': 13,
'4Da': 13,
'15a': 11,
'3Dista': 11,
'4m': 10,
'5Da': 9,
'6m': 8,
'7m': 6,
'4Dista': 6,
'2Dista': 6,
'0m': 6,
'9m': 6,
'17a': 4,
'1Dpga': 4,
'5Dista': 4,
'6Da': 4,
'5m': 4,
'Dista': 4,
'1Disqa': 4,
'2Dpga': 4,
'7Dista': 3,
'10m': 3,
'16a': 2,
'12m': 2,
'6Dista': 2,
'8m': 2,
'3Dm': 2,
'11m': 2,
'Rpa': 2,
'Am': 1,
'7Da': 1,
'10Dista': 1,
'2Dm': 1,
'2Disqa': 1,
'Ta': 1,
'3Dpga': 1,
'4Distm': 1,
'1Dm': 1,
'8Dista': 1}
Le dictionnaire contient le nombre d’observations par catégorie dans M1.
# remplacer les étiquettes par les chiffres
X_train['M1'] = X_train['M1'].map(count_map)
X_test['M1'] = X_test['M1'].map(count_map)
# Résultat
X_train['M1'].head(10)
2540 642
19008 5015
7624 362
2575 2250
852 514
4855 5015
8154 5015
18928 5015
18286 514
15712 904
Name: M1, dtype: int64
# si au lieu du comptage nous souhaitons la fréquence
# il suffit de diviser le compte par le nombre total d'observations :
frequency_map = (X_train['M1'].value_counts() / len(X_train) ).to_dict()
frequency_map
{5015: 0.3494286510590858,
2250: 0.15677257525083613,
1744: 0.12151616499442586,
1415: 0.09859253065774805,
904: 0.06298773690078038,
642: 0.044732441471571904,
514: 0.03581382385730212,
362: 0.025222965440356744,
306: 0.021321070234113712,
258: 0.01797658862876254,
190: 0.013238573021181716,
122: 0.008500557413600892,
102: 0.007107023411371237,
72: 0.005016722408026756,
59: 0.00411092530657748,
44: 0.0030657748049052395,
32: 0.002229654403567447,
4: 0.002229654403567447,
6: 0.0020903010033444815,
28: 0.0019509476031215162,
13: 0.0018115942028985507,
25: 0.001741917502787068,
24: 0.0016722408026755853,
11: 0.0015328874024526198,
21: 0.001463210702341137,
20: 0.0013935340022296545,
19: 0.0013238573021181717,
17: 0.0011845039018952062,
2: 0.0009754738015607581,
1: 0.0006967670011148272,
10: 0.0006967670011148272,
9: 0.0006270903010033445,
8: 0.0005574136008918618,
3: 0.0004180602006688963}
# remplacer les étiquettes par les fréquences
X_train['M1'] = X_train['M1'].map(frequency_map)
X_test['M1'] = X_test['M1'].map(frequency_map)
Utilisation avec Feature-Engine
data['idJockey'] = data['idJockey'].astype(object)
data['M1'] = data['M1'].astype(object)
X_train, X_test, y_train, y_test = train_test_split(
data[['M1', 'M2', 'Hippodrome']],
data['place'],
test_size=0.3,
random_state=0)
X_train.shape, X_test.shape
count_enc = CountFrequencyCategoricalEncoder(
encoding_method='count', # Pour la fréquence ==> encoding_method='frequency'
variables=['M1', 'M2', 'Hippodrome'])
count_enc.fit(X_train)
CountFrequencyCategoricalEncoder(variables=['M1', 'M2', 'Hippodrome'])
# On retrouve fort heureusement les mêmes valeurs quavec Pandas
count_enc.encoder_dict_
{'M1': {'1a': 5015,
'2a': 2250,
'Da': 1744,
'3a': 1415,
'4a': 904,
'5a': 642,
'6a': 514,
'7a': 362,
'8a': 306,
'9a': 258,
'10a': 190,
'0a': 122,
'11a': 102,
'Dm': 72,
'12a': 59,
'13a': 44,
'1m': 32,
'2Da': 28,
'1Da': 25,
'14a': 24,
'3Da': 21,
'3m': 20,
'Aa': 19,
'2m': 17,
'1Dista': 13,
'4Da': 13,
'15a': 11,
'3Dista': 11,
'4m': 10,
'5Da': 9,
'6m': 8,
'7m': 6,
'4Dista': 6,
'2Dista': 6,
'0m': 6,
'9m': 6,
'17a': 4,
'1Dpga': 4,
'5Dista': 4,
'6Da': 4,
'5m': 4,
'Dista': 4,
'1Disqa': 4,
'2Dpga': 4,
'7Dista': 3,
'10m': 3,
'16a': 2,
'12m': 2,
'6Dista': 2,
'8m': 2,
'3Dm': 2,
'11m': 2,
'Rpa': 2,
'Am': 1,
'7Da': 1,
'10Dista': 1,
'2Dm': 1,
'2Disqa': 1,
'Ta': 1,
'3Dpga': 1,
'4Distm': 1,
'1Dm': 1,
'8Dista': 1},
'M2': {'1a': 4258,
'Da': 2096,
'2a': 1966,
'3a': 1336,
'4a': 951,
'5a': 756,
'6a': 588,
'7a': 490,
'8a': 402,
'9a': 299,
'10a': 267,
'0a': 179,
'11a': 138,
'Dm': 85,
'12a': 82,
'13a': 59,
'14a': 38,
'1m': 36,
'1Da': 30,
'2Da': 27,
'3m': 24,
'Aa': 21,
'2m': 21,
'6m': 16,
'3Da': 16,
'5m': 14,
'15a': 12,
'8m': 11,
'4Da': 10,
'4m': 10,
'6Da': 9,
'9m': 8,
'Dista': 7,
'7m': 7,
'1Dista': 7,
'10m': 6,
'5Dista': 6,
'3Dista': 6,
'5Da': 6,
'2Dista': 5,
'16a': 4,
'4Dm': 3,
'11m': 3,
'Am': 3,
'5Dm': 3,
'2Dpga': 3,
'4Dista': 2,
'1Disqa': 2,
'17a': 2,
'1Dpga': 2,
'6Dista': 2,
'Ta': 2,
'12m': 2,
'3Disqa': 1,
'3Distm': 1,
'8Da': 1,
'0m': 1,
'7Da': 1,
'Dpga': 1,
'13m': 1,
'14m': 1,
'9Da': 1,
'8Dm': 1,
'8Dista': 1,
'12Dist.a': 1,
'1Dm': 1,
'2Dm': 1},
'Hippodrome': {'Vincennes': 2712,
'Cagnes-sur-Mer': 773,
'Enghien': 669,
'Mons': 661,
'Avenches': 500,
'Cabourg': 440,
'Vichy': 427,
'Laval': 385,
'Caen': 369,
'Mauquenchy': 340,
'Wolvega': 325,
'Toulouse': 323,
'Marseille-Vivaux': 273,
'La Capelle': 223,
'Graignes': 219,
'Reims': 213,
'Le Croisé-Laroche': 203,
'Amiens': 181,
'Agen': 178,
'Beaumont-de-Lomagne': 177,
'Lyon-La Soie': 176,
'Marseille-Borély': 173,
'Hyères': 172,
'Saint-Galmier': 168,
'Pornichet': 167,
'Nantes': 154,
'Chartres': 154,
'Feurs': 149,
'Lyon-Parilly': 143,
'Bordeaux Le Bouscat': 141,
'Meslay-du-Maine': 135,
'Les Sables-d'Olonne': 129,
'Argentan': 128,
'Angers': 118,
'Le Mont-Saint-Michel': 90,
'Chatelaillon-La Rochelle': 89,
'Cherbourg': 87,
'Lisieux': 86,
'Le Mans': 84,
'Saint-Malo': 81,
'Châteaubriant': 80,
'Cavaillon': 77,
'Vire': 76,
'Pontchâteau': 74,
'Cordemais': 73,
'Solvalla': 72,
'Divonne-les-Bains': 63,
'Aby': 61,
'Strasbourg': 52,
'Maure-de-Bretagne': 52,
'Saint-Brieuc': 51,
'Nancy': 46,
'Langon-Libourne': 45,
'Bjerke': 44,
'Cagnes': 42,
'Jägersro': 37,
'Craon': 36,
'Kuurne': 36,
'Tongres': 35,
'Gelsenkirchen': 35,
'Bergsaker': 33,
'Son Pardo Majorque': 31,
'Farjestad': 30,
'Cholet': 29,
'Eskilstuna': 29,
'Axevalla': 27,
'Salon-de-Provence': 25,
'Marseille': 22,
'Halmstad': 22,
'Ostersund': 22,
'Duindigt': 21,
'Waregem': 20,
'Hagmyren': 18,
'Rambouillet': 16,
'Umaker': 16,
'Orebro': 16,
'Jarlsberg': 16,
'Gavle': 16,
'Munich-Daglfing': 15,
'Lyon': 13,
'Clairefontaine': 13,
'Mantorp': 13,
'Romme': 13,
'Royan-la Palmyre': 12,
'Bordeaux': 12,
'Boden': 11,
'Rattvik': 11,
'Straubing': 11,
'Bréhal': 11,
'Dannero': 10,
'Bernay': 10,
'Alençon': 10,
'Bollnas': 10,
'Saint': 10,
'Lignières': 10,
'Ecommoy': 10,
'Montier-en-Der': 10,
'Leangen': 9,
'Melton': 9,
'Sablé-sur-Sarthe': 9,
'Berlin-Mariendorf': 9,
'Le Croisé': 9,
'Aalborg': 8,
'Singapour': 8,
'Vaggeryd': 8,
'Arras': 8,
'Bergen': 8,
'Carentan': 8,
'Arjang': 8,
'Oraison': 8,
'Aix-les-Bains': 8,
'La Roche-Posay': 8,
'L'Isle-sur-La Sorgue': 7,
'Castera-Verduzan': 7,
'Segré': 7,
'Le Touquet': 7,
'Skelleftea': 6,
'Angoulême': 6,
'Montauban': 6,
'Dieppe': 6,
'Biarritz': 6,
'Paray-le-Monial': 6,
'Frauenfeld': 6,
'Marsa': 6,
'Vannes': 6,
'Nîmes': 6,
'Vitré': 6,
'Castillonnès': 6,
'Auch': 6,
'Montluçon-Néris-les-Bains': 6,
'Villeneuve-sur-Lot': 6,
'Agon-Coutainville': 5,
'La Gacilly': 5,
'Odense': 5,
'Niort': 5,
'Avignon': 5,
'Saint-Jean-de-Monts': 5,
'Chatillon-sur-Chalaronne': 5,
'Kalmar': 5,
'Villeréal': 5,
'Son Pardo': 5,
'Saint-Omer': 4,
'Forus': 4,
'Biri': 4,
'Hambourg Bahrenfeld': 4,
'Grenade-sur-Garonne': 4,
'Fougères': 4,
'Carpentras': 4,
'Tours-Chambray': 4,
'Zonza': 4,
'Vermo': 4,
'Vittel': 4,
'Maure': 4,
'Beaumont': 4,
'Challans': 4,
'Skive': 4,
'Harstad': 4,
'Ajaccio': 3,
'Baden': 3,
'Amal': 3,
'Dundalk': 3,
'Momarken': 3,
'Visby': 3,
'Erbray': 3,
'Prunelli-di-Fiumorbo': 3,
'Landivisiau': 3,
'Eauze': 3,
'Nort-sur-Erdre': 2,
'Ebreichsdorf (Magna Racino)': 2,
'Meslay': 2,
'Machecoul': 2,
'Vienne Krieau': 2,
'Charlottenlund': 2,
'Ostende': 2,
'Meadowlands': 2,
'Kouvola': 2,
'Laon': 2,
'Mikkeli': 2,
'Geelong': 1,
'La Roche': 1,
'Chatillon': 1,
'Saint-Moritz': 1,
'(Q+ du mardi 25': 1,
'(R1C5) lundi 14': 1,
'Dinslaken': 1,
'Hambourg Horn': 1,
'Chatelaillon': 1,
'Tours': 1,
'(R1C1) Vendredi 04': 1,
'Loudéac': 1,
'Montluçon': 1,
'Ballarat': 1,
'Yonkers-New York': 1,
'lundi 02': 1,
'Dielsdorf': 1}}
X_train = count_enc.transform(X_train)
X_test = count_enc.transform(X_test)
# Explorons le résultat
X_train.head()
M1 M2 Hippodrome
2540 642 756 440
19008 5015 1336 80
7624 362 2096 2712
2575 2250 179 440
852 514 756 178
**Note
Si la variable argument est laissée à None, le codeur identifiera automatiquement toutes les variables catégorielles.
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 le codeur.
Notez que s’il y a une variable dans le jeu de test, pour laquelle l’encodeur n’a pas de numéro à attribuer (la catégorie n’a pas été vue dans le jeu de train), l’encodeur retournera une erreur.
Je télécharge mon guide gratuit
Thank you!
You have successfully joined our subscriber list.
Vous recevrez votre guide par email sans aucun engagement de votre part.