Des étiquettes qui se produisent rarement
Les variables catégorielles sont celles dont les valeurs sont sélectionnées dans un groupe de catégories, également appelées labels. Différents labels apparaissent dans l’ensemble de données avec des fréquences différentes. Certaines catégories apparaissent beaucoup dans l’ensemble de données, tandis que d’autres catégories n’apparaissent que dans un nombre limité d’observations.
Par exemple, dans un ensemble de données contenant des informations sur les demandeurs de prêts dont l’une des variables est la “ville” où vit le demandeur, des villes comme “Paris” peuvent apparaître en grand nombre dans les données parce que Paris a une population très importante, alors que des villes plus petites comme “Bez et esparon” n’apparaîtront qu’à quelques reprises (population < 2000 personnes), parce que la population y est très faible. Un emprunteur est plus susceptible de vivre à Paris, car la population y est beaucoup plus importante.
En fait, les variables catégorielles contiennent souvent quelques étiquettes dominantes qui représentent la majorité des observations et un grand nombre d’étiquettes qui n’apparaissent que rarement.
Les étiquettes rares dans une variable catégorielle posent-elles un problème ?
Les valeurs rares peuvent ajouter beaucoup d’informations, voire aucune. Prenons par exemple une assemblée d’actionnaires où chaque personne peut voter proportionnellement au nombre de ses actions. L’un des actionnaires possède 50 % des actions, et les 999 autres actionnaires possèdent les 50 % restants. Le résultat du vote est largement influencé par l’actionnaire qui détient la majorité des actions. Les autres actionnaires peuvent avoir un impact collectif, mais ils n’ont pratiquement aucun impact individuel.
La même chose se produit dans les ensembles de données de la vie réelle. L’étiquette qui est surreprésentée dans l’ensemble de données a tendance à dominer le résultat, et ceux qui sont sous-représentés peuvent n’avoir aucun impact individuellement, mais pourraient avoir un impact s’ils sont considérés collectivement.
Plus précisément,
Les valeurs rares des variables catégorielles ont tendance à provoquer un surajustement, en particulier dans les méthodes basées sur les arbres.
Un grand nombre d’étiquettes peu fréquentes ajoutent du bruit, avec peu d’informations, provoquant ainsi un sur-ajustement.
Des étiquettes rares peuvent être présentes dans la série d’entraînement, mais pas dans la série de test, ce qui entraîne un surdimensionnement de la série d’entraînement.
Des étiquettes rares peuvent être présentes dans l’ensemble de test, mais pas dans l’ensemble d’entrainement. Ainsi, le modèle d’apprentissage en machine ne saura pas comment l’évaluer.
Note Des valeurs parfois rares, sont en effet importantes. Par exemple, si nous construisons un modèle pour prédire les demandes de prêts frauduleux, qui sont par nature rares, alors une valeur rare dans une certaine variable, peut en effet être très prédictive. Cette valeur rare pourrait nous indiquer que l’observation est très probablement une demande frauduleuse, et nous choisirions donc de ne pas l’ignorer.
Dans ce notebook :
Nous allons essayer:
- Apprendre à identifier les étiquettes rares dans un ensemble de données
- Comprendre combien il est difficile d’en tirer des informations fiables.
- Visualiser la répartition inégale des étiquettes rares entre les trains et les bancs d’essai
Je dis essayer car je ne sais pas du tout si nous avons des étiquettes rares.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# to separate data intro train and test sets
from sklearn.model_selection import train_test_split
data = pd.read_csv('../exportfeature.csv',parse_dates=['date'],infer_datetime_format=True,dayfirst=True,sep=";",encoding='ANSI')
data.head()
id place rapport date M1 M2 M3 M4 allocation Hippodrome nbPartants idHippodrome nDistance nAllocation idFerrure idJockey nbPartants.1 rPoids idOeillere iCote
0 56005 1 1.3 2016-01-01 3a 3m Da 3a 90000.0 Vincennes 17 Vincennes 2100 95000 0 18 17 0 0 12.0
1 98833 1 2.2 2016-01-01 3a 9Da 5a 4a 70000.0 Vincennes 18 Vincennes 2700 58000 1 30 18 0 0 12.0
2 98834 0 0.0 2016-01-01 1a Da Da 1a 44000.0 Vincennes 12 Vincennes 2850 37000 0 53 10 0 0 9.0
3 98835 1 1.5 2016-01-01 Da 7a 4a 2a 40000.0 Vincennes 11 Vincennes 2200 38000 2 143 13 0 0 4.0
4 98836 0 0.0 2016-01-01 1a 7a 10a 11a 40000.0 Vincennes 16 Vincennes 2700 34000 0 50 17 0 0 8.0
# examinons les différents nombres de labels
# dans chaque variable (cardinalité)
# ce sont les variables catégorielles chargées
cat_cols = ['M1', 'M2', 'M3', 'M4', 'Hippodrome','idHippodrome','idJockey']
for col in cat_cols:
print('variable: ', col, ' number de variations: ', data[col].nunique())
print('Taille de notre dataframe: ', len(data))
variable: M1 number de variations: 67
variable: M2 number de variations: 71
variable: M3 number de variations: 72
variable: M4 number de variations: 69
variable: Hippodrome number de variations: 199
variable: idHippodrome number de variations: 303
variable: idJockey number de variations: 1943
Taille de notre dataframe: 20504
La variable jockey montre 1943 états différents.
# traçons la fréquence de chaque étiquette
# apparaît dans l'ensemble de données
# en d'autres termes, le pourcentage de maisons dans les données
# avec chaque étiquette
total_chevaux = len(data)
# pour chaque variable
for col in cat_cols:
# compter le nombre de chevaux par catégorie
# et diviser par le nombre total de maisons
# alias pourcentage de chevaux par catégorie
temp_df = pd.Series(data[col].value_counts() / total_chevaux)
# création du graph
fig = temp_df.sort_values(ascending=False).plot.bar()
fig.set_xlabel(col)
# ajout d'une ligne à 2 % pour marquer le seuil pour les catégories rares
fig.axhline(y=0.02, color='red')
fig.set_ylabel('Pourcentage de chevaux')
plt.show()
Pour chacune des variables catégorielles, certains labels apparaissent dans plus de 10 % des chevaux et beaucoup apparaissent dans moins de 10 % ou même 5 % des chevaux. Il s’agit d’étiquettes peu fréquentes ou de valeurs rares qui pourraient entraîner un sur-ajustement.
Quel est le rapport entre l’objectif “Place” et ces catégories ?
Dans les cellules suivantes, je veux comprendre la place à l’arrivée en fonction de l’étiquettes. Pour rappel, place vaut 1 quand le cheval est dans les 3 premiers et 0 dans les autres cas.
Continuez à lire, cela deviendra plus clair.
# la fonction suivante calcule :
# 1) le pourcentage de chevaux par catégorie
# 2) la cote moyenne par catégorie
def calculate_mean_target_per_category(df, var):
# taille du df
total_chx = len(df)
# pourcentage de chevaux par catégorie
temp_df = pd.Series(df[var].value_counts() / total_chx).reset_index()
temp_df.columns = [var, 'perc_chx']
# place moyenne par catégorie
temp_df = temp_df.merge(df.groupby([var])['place'].mean().reset_index(),
on=var,
how='left')
return temp_df
# maintenant nous utilisons la fonction pour la variable "idJockey".
temp_df = calculate_mean_target_per_category(data, 'M1')
temp_df
M1 perc_chx place
0 1a 0.352468 0.601218
1 2a 0.152604 0.562480
2 Da 0.121684 0.475351
3 3a 0.095494 0.536772
4 4a 0.063402 0.523077
... ... ... ...
62 2Disqa 0.000049 0.000000
63 Dpga 0.000049 0.000000
64 Am 0.000049 0.000000
65 9Da 0.000049 1.000000
66 8Dista 0.000049 1.000000
67 rows × 3 columns
Le cadre de données ci-dessus contient le pourcentage de chevaux qui affichent chacune des étiquettes du jockey.
# Maintenant je crée une fonction pour tracer de la
# fréquence de la catégorie et la cote moyenne.
# Cela nous aidera à visualiser la relation entre la
# cible et les étiquettes de la variable catégorielle
def plot_categories(df, var, seuil=0.02):
fig, ax = plt.subplots(figsize=(8, 4))
plt.xticks(df.index, df[var], rotation=90)
ax2 = ax.twinx()
ax.bar(df.index, df["perc_chx"], color='lightgrey')
ax2.plot(df.index, df["place"], color='green', label='Seconds')
ax.axhline(y=seuil, color='red')
ax.set_ylabel('Pourcentage')
ax.set_xlabel(var)
ax2.set_ylabel('place moyenne')
plt.show()
plot_categories(temp_df, 'M1')
Ce que nous pouvons voir sur ce graphique c’est que plus la place à l’arrivée décroit en fonction de la position du cheval à l’arrivée dans sa dernière course. Je sais j’enfonce une porte ouverte mais cela est utile à visualiser. Par contre on ne peut rien tirer comme infos supplémentaires utilisables.
for col in cat_cols:
if col !='idJockey':
temp_df = calculate_mean_target_per_category(data, col)
plot_categories(temp_df, col)
Étiquettes rares : regroupement sous une nouvelle étiquette
Une façon courante de travailler avec des valeurs rares ou peu fréquentes est de les regrouper sous une catégorie appelée “Rare” ou “Autre”. De cette façon, nous sommes en mesure de comprendre l’effet “collectif” des étiquettes peu fréquentes sur la cible.
# Je remplacerai tous les labels qui apparaissent dans moins de 2%
# Nombre de maisons classées "rares".
def group_rare_labels(df, var,pourcentage=0.02):
total_chx = len(df)
# Je calcule d'abord le % de chevaux pour chaque catégorie
temp_df = pd.Series(df[var].value_counts() / total_chx)
# maintenant je crée un dictionnaire pour remplacer les étiquettes rares par les
# chaîne "rare" si elles sont présentes dans moins de 2% des chevaux
grouping_dict = {
k: ('rare' if k not in temp_df[temp_df >= pourcentage].index else k)
for k in temp_df.index
}
# je remplace les catégories
tmp = df[var].map(grouping_dict)
return tmp
# groupe rare M1
data['M1_groupe'] = group_rare_labels(data, 'M1')
data[['M1', 'M1_groupe']].head(10)
M1 M1_groupe
0 3a 3a
1 3a 3a
2 1a 1a
3 Da Da
4 1a 1a
5 8a 8a
6 2a 2a
7 9a rare
8 1a 1a
9 4a 4a
# traçons avec les catégories groupées
# en réutilisant les fonctions que j'ai créées ci-dessus
temp_df = calculate_mean_target_per_category(data, 'M1_groupe')
plot_categories(temp_df, 'M1_groupe')
“Rare” contient maintenant l’influence globale de toutes les catégories peu fréquentes sur la place.
# traçons le graphe d'origine à des fins de comparaison
temp_df = calculate_mean_target_per_category(data, 'M1')
plot_categories(temp_df, 'M1')
Seules 10 catégories de M1 sont relativement courantes dans l’ensemble des données. Les autres sont maintenant regroupées dans la catégorie “rare”. On voit clairement que la cétagorie rare est positionnée nettement moisn bien que les autres catégories
# Analysons les autres variables
for col in cat_cols[1:]:
# re using the functions I created
data[col+'_grouped'] = group_rare_labels(data, col)
temp_df = calculate_mean_target_per_category(data, col+'_grouped')
plot_categories(temp_df, col+'_grouped')
Pour les M2 à M4 on peut voir clairement que la catégorie rare sous performe nettement. Pour les hippodromes et le jockey, la catégorie rare avec un seuil de 2% ne semble pas adaptée.
#Si je prends un seuil de 0,003 on peut commencer à voir des choses très interessantes
for col in cat_cols[1:]:
if col=='idJockey' or col=='Hippodrome' or col=='idHippodrome':
data[col+'_grouped'] = group_rare_labels(data, col,0.003)
temp_df = calculate_mean_target_per_category(data, col+'_grouped')
plot_categories(temp_df, col+'_grouped',0.003)
Il y a vraiment des hippodromes et des jockeys qui ont une “mauvaise” influence sur le résultat des chevaux.