Statistiques descriptives avec Pandas

Objectif pédagogique : Maîtriser l’analyse exploratoire des données numériques avec les indicateurs de position et de dispersion.

À la fin de ce chapitre, tu seras capable de…

  • Calculer les statistiques descriptives fondamentales (moyenne, médiane, quantiles)
  • Interpréter les écarts entre moyenne et médiane pour détecter l’asymétrie des distributions
  • Analyser la répartition des données à travers les indicateurs de position
  • Utiliser la méthode describe() de Pandas pour explorer efficacement un jeu de données

💡 Prérequis : Maîtrise des bases de Pandas (DataFrame, lecture de fichiers CSV, sélection de colonnes)

Mise en contexte

Vous disposez d’un fichier de réservations de véhicules avec des milliers de lignes. Comment rapidement comprendre la structure de vos données ? Découvrez comment une simple différence entre moyenne (4507€) et médiane (1136€) révèle des insights business cruciaux sur votre portefeuille clients. Vous apprendrez à détecter les distributions asymétriques et à interpréter correctement vos indicateurs financiers.

INTRODUCTION :  En Data Science, avant de commencer le processing et la modélisation des données, il est très important de se familiariser avec le jeu de données dont nous disposons. Pour cela, l’analyse descriptive des variables numériques au travers d’indicateurs simples apparaît comme essentielle et permet de faire une première bonne exploration.
Nous pouvons distinguer deux familles : les indicateurs de position et les indicateurs de dispersion. Pandas, à travers sa méthode describe, offre un outil permettant d’obtenir rapidement les statistiques descriptives de variables quantitatives. L’objectif de ce cours est de comprendre quelques statistiques présentées dans l’outil describe et d’aller un peu plus loin dans leur interprétation et leur utilisation.
  • Importer les packages pandas et numpy
  • Charger dans un DataFrame nommé df_reservations les données contenues dans le fichier ‘reservations.csv’ à télécharger ici.
  • Afficher les 5 premières lignes de data.
import pandas as pd
import numpy as np
df_reservations = pd.read_csv("reservations.csv")
df_reservations.head()

1 – Statistiques descriptives

  • Déterminer les variables numériques et les stocker dans un tableau num_data.
num_data = df_reservations.select_dtypes(include=['int64','float64'])

Il existe certaines statistiques descriptives simples à calculer qui permettent d’obtenir une meilleure compréhension des données quantitatives. C’est ce qu’on appelle les indicateurs de position. Ils sont au nombre de cinq : moyenne, médiane, quantiles, min, max. La moyenne permet de résumer facilement une liste de données numériques en une seule valeur. Elle se calcule avec la formule suivante :

où   

est la moyenne, N la longueur de la liste et     les éléments de la liste.

  • Calculer la moyenne de chaque variable contenue dans num_data et les stocker dans un DataFrame stats avec comme unique colonne moyenne.
  • Comparer la moyenne des montants des location des véhicules (montant_location) avec celle des montants des location avec options (montant_total_avec_options). Que constate-t-on ? Que peut-on dire des moyennes de duree_initiale_jours et de jours_retard ?

Pour la création du DataFrame stats, on pourra utiliser pd.DataFrame et l’attribut columns. Pour le calcul des moyennes, on pourra s’aider de pandas.DataFrame.mean().

stats = pd.DataFrame(num_data.mean(), columns=['moyenne'])

stats.round(2) # Afficher seulement 2 chiffres après la virgule.

Le montant de location (hors options) est en moyenne de 4 507,29 euros. Il constitue la base de facturation avant ajout des services complémentaires et reflète directement la durée fois le prix journalier appliqué.
Chaque réservation génère en moyenne 5 716,74 euros de revenu total, options incluses. Ce montant intègre la location de base, les options journalières et toute assurance premium souscrite.
Les options et services complémentaires représentent en moyenne +1 209,45 euros par réservation, soit une majoration de 26,8% par rapport au montant de location hors options. Cela signifie qu’un client dépense en moyenne près d’un quart de plus que le tarif de base grâce aux options. Cela représente une source de revenus significative pour l’entreprise.
La durée moyenne d’un contrat de location (duree_initiale_jours) est de 73,79 jours (2,5 mois). Ce chiffre reflète une forte proportion de contrats longue durée (LD) dans le portefeuille, typiquement conclus avec des entreprises ou des clients VIP. En moyenne, les véhicules sont restitués avec 1,60 jour de retard (jours_retard). Rapporté à la durée initiale du contrat, ce retard représente seulement environ 2,2% de la durée totale.
La médiane d’un ensemble de valeurs est la valeur x qui permet de couper l’ensemble des valeurs en deux parties égales : mettant d’un côté une moitié des valeurs, qui sont toutes inférieures ou égales à x et de l’autre côté l’autre moitié des valeurs, qui sont toutes supérieures ou égales à x (s’il y a un nombre impair de valeurs, la valeur centrale sera mise des deux côtés).
La moyenne est une bonne entrée en matière, cependant elle ne permet pas d’avoir une répartition précise des données. Pour cela, la médiane est un indicateur plus précis.
  • Calculer la médiane de chaque variable contenue dans num_data et ajouter une colonne median à stats.
  • Interpréter ces valeurs pour chaque variable en vous servant de la définition donnée précédemment.
Pour le calcul des médianes, on pourra s’aider de pandas.DataFrame.median().
stats['median'] = num_data.median()
stats

Pour la durée des réservations (duree_initiale_jours), la médiane à 19 jours révèle que la moitié des contrats durent moins de 3 semaines. La moyenne de 73,79 j est fortement tirée vers le haut par une minorité de contrats longue durée. La distribution est très asymétrique à droite ce qui montre que la moyenne est ici trompeuse. Pour le montant de location (hors options), l’écart est énorme. En effet, la médiane à 1 136 € révèle que la moitié des locations coûtent moins de 1 200 €. La moyenne de 4 507 € est gonflée par des contrats longue durée à très fort montant. La moyenne serait un indicateur très biaisé ici.

  • Créer une variable mean_med_diff correspondant à la valeur absolue de la différence entre moyenne et médiane.
stats['mean_med_diff'] = abs(stats['moyenne'] - stats['median'])

stats.round(2)

Pour donner une interprétation de cette nouvelle variable, il faut bien avoir en tête les ordres de grandeur de chaque variable.

Pour confirmer notre analyse, il est nécessaire de regarder les quantiles de chaque variable.

Les quantiles sont les valeurs qui divisent un jeu de données en intervalles contenant le même nombre de données. De manière générale, on divise le jeu de données en 4 de telle sorte que le premier quantile renvoie la valeur à partir de laquelle on retrouve 25% des valeurs et ainsi de suite pour les autres quantiles.

  • Déterminer les quantiles de montant_total_avec_options.

Pour déterminer les quantiles, nous pourrions nous aider de pandas.DataFrame.quantile(q =[0.25,0.5,0.75]).

quant_montant_total_avec_options = num_data[‘montant_total_avec_options’].quantile(q = [0.25, 0.5, 0.75])

quant_montant_total_avec_options

  • Ajouter au tableau stats 3 colonnes q1, q2 et q3 renseignant respectivement le premier, deuxième et troisième quantile de chaque variable numérique.
stats[['q1', 'q2', 'q3']] = num_data.quantile(q=[0.25, 0.5, 0.75]).transpose()

stats

  • Afficher stats, comparer q2 et q3
  • Comparer q3 et la moyenne
  • Cela confirme-t-il notre analyse précédente ?
print('q2 < q3 :\n', stats['q2'] < stats['q3'])

print('\nq3 < moyenne :\n', stats['q3'] < stats['moyenne'])

Les résultats montrent que :

  • Nous avons q2 < q3 (=True partout), ce qui signifie que nous avons une distribution normale et attendue.
  • q3 < moyenne (= False partout). Cela révèle que pour toutes les variables, la moyenne est supérieure au 3ème quartile, ce qui signifie :
    • qu’il y a des valeurs extrêmes très élevées
    • que la moyenne n’est pas représentative
    • qu’il ya nécessité d’utiliser la médiane
    • qu’il y a besoin de segmenter l’analyse

On peut compléter notre analyse en s’intéressant au min, max de chaque variable. La différence des deux nous donnera une idée de l’étendue sur laquelle se répartissent les valeurs.

  • Ajouter au tableau stats 3 colonnes min, max et min_max_diff renseignant le min, le max et la différence des deux.
  • Que remarquez-vous sur la différence des deux ?

💡 À retenir : Les écarts entre min et max sont énormes (facteurs de 100, 1000, voire 3000). Ces grandes étendues sont principalement dues aux valeurs maximales extrêmes. Les données couvrent des cas ultra-variés, du client économique (particuliers) au premium. Il est donc impossible de traiter ces données comme un ensemble homogène. Une segmentation est donc nécessaire.

Deux derniers indicateurs nous permettent d’avoir un bon aperçu : la variance et l’écart-type. Ils font partie de la famille des indicateurs de dispersion :

  • La variance, qui mesure la dispersion autour de la moyenne, c’est à dire à quel point les valeurs présentes dans une liste numérique sont écartées de la moyenne. Elle se définit mathématiquement comme suit :

où        est la moyenne, N la longueur de la liste et  les éléments de la liste.

L’écart type, qui se définit mathématiquement comme la racine carrée de la variance. De par sa construction, l’écart type est également une mesure de la dispersion des valeurs.

Nous pouvons calculer facilement l’écart type d’une variable en utilisant pandas.Series.std()

  • Calculer l’écart type de la variable montant_total_avec_options.
  • Afficher l’intervalle moyenne +/- ecart type de la variable montant_total_avec_options.
ecart_type = num_data['montant_total_avec_options'].std()

print("Ecart-type de montant_total_avec_options :", ecart_type)

intervalle = [
    float(num_data['montant_total_avec_options'].mean() - ecart_type),
    float(num_data['montant_total_avec_options'].mean() + ecart_type)
]

print("Intervalle :", intervalle)

Résultat :

Ecart-type de montant_total_avec_options : 9526.147266190683
Intervalle : [-3809.4040401906823, 15242.890492190683]
L’écart-type montre que les montants varient beaucoup. La borne négative de l’intervalle montre que cet intervalle est inapproprié. Il est donc préférable d’utiliser la médiane et les quartiles pour analyser correctement les prix de location.
  • Afficher la médiane et les quartiles de la variables montant_total_avec_options
  • Calculer l’intervalle inter-quartile et interpréter les valeurs.
median = float(df_reservations['montant_total_avec_options'].median())
q1 = float(df_reservations['montant_total_avec_options'].quantile(0.25))
q3 = float(df_reservations['montant_total_avec_options'].quantile(0.75))
iqr = float(q3 - q1)

print("Q1 :", np.round(q1, 2))
print("Médiane :", np.round(median, 2))
print("Q3 :", np.round(q3, 2))
print("IQR :", np.round(iqr, 2))

Résultats :

Q1 : 500.84
Médiane : 1388.6
Q3 : 6856.56
IQR : 6355.71
Cela signifie que 50% des clients paient entre 500,84 euros et 6 856,56 euros. Ces valeurs sont toutes positives et réalistes. Elles ne sont pas influencées par les cas extrêmes.
  • Ajouter une nouvelle colonne intitulée montant_total_avec_options_in à num_data contenant 1 si la valeur de montant_total_avec_options appartiennent à l’iqr calculé précédemment et 0 sinon.

Pour déterminer les valeurs qui appartiennent à l’intervalle calculé précédemment, on pourra s’aider de pandas.Series.between(int1, int2).astype(int). Pour afficher les proportions on pourra s’aider de pandas.Series.value_counts(normalize = True).

num_data['montant_total_avec_options_in'] = num_data['montant_total_avec_options'].between(q1, q3).astype(int)
num_data['montant_total_avec_options_in'].value_counts(normalize = True)

Nous voyons évidemment que 50% des prix de réservations sont dans l’intervalle interquartile (ce résultat est logique et évident).

  • Établir les « bornes » au-delà desquelles les prix (montant_total_avec_options) sont considérés comme aberrants.

Les bornes inférieures et supérieures sont calculées par :

borne_inf = Q1 – 1,5IQR

borne_sup = Q3 + 1.5IQR

Toute valeur inférieure à borne_inf ou supérieure à borne_sup est considérée comme valeur aberrante.

Définition des bornes

borne_inf = q1 – 1.5 * iqr

borne_sup = q3 + 1.5 * iqr

Identification des outliers

outliers = num_data[(num_data[‘montant_total_avec_options’] < borne_inf) | (num_data[‘montant_total_avec_options’] > borne_sup)]

print(f »Nombre de valeurs aberrantes : {len(outliers)} »)

💡 À retenir : Nombre de valeurs aberrantes : 587