Caractéristiques des variables : les étiquettes rares

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()
m1
m2
m3
m4
hippodrome
idhippodrome
idjockey

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')
m1 categ

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)
m1rare
m2rare
m3rare
m4rare
hipporare
idhipporare

É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')
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')
m1 suite

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')
m2 grp
m3 grp
m4 grp
hipo grp
idhipo grp
idjockey grp

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)
hipo2 grp
idhip2 grp
idjockey2 grp

Il y a vraiment des hippodromes et des jockeys qui ont une “mauvaise” influence sur le résultat des chevaux.

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.