plt.figure(figsize=(10, 6))
sns.scatterplot(data=df_titanic, x='age', y='fare')
plt.title("Nuage de points : Âge vs Tarif du Billet")
plt.xlabel("Âge (années)")
plt.ylabel("Tarif ($)")
plt.show()
1 Introduction à l’Analyse Statistique Descriptive Bivariée
Après avoir exploré les variables une par une (analyse univariée), l’étape suivante de toute analyse de données consiste à examiner les relations entre les variables. C’est le domaine de l’analyse bivariée. Son but est de comprendre comment deux variables interagissent, s’influencent ou évoluent ensemble.
Cette analyse est fondamentale pour formuler des hypothèses, découvrir des tendances et préparer le terrain pour des modélisations plus complexes. Par exemple, l’âge d’un passager a-t-il un lien avec le prix de son billet ? Le sexe influence-t-il les chances de survie ? L’analyse bivariée nous aide à répondre à ces questions.
Objectifs de ce tutoriel :
À la fin de ce tutoriel, vous serez capable de :
- Analyser la relation entre deux variables quantitatives en utilisant des nuages de points et en calculant des coefficients de corrélation.
- Étudier le lien entre deux variables qualitatives grâce aux tableaux de contingence et aux graphiques associés.
- Comparer une variable quantitative à travers les catégories d’une variable qualitative à l’aide de boxplots et de comparaisons de moyennes.
- Mettre en œuvre ces techniques avec les bibliothèques Python
Pandas,MatplotlibetSeaborn.
2 Deux Variables Quantitatives
L’objectif ici est d’étudier la relation entre deux variables numériques, que l’on peut représenter par une série de paires \((x_i, y_i)_{i=1}^n\). On cherche à savoir si les variations de l’une sont liées aux variations de l’autre.
2.1 1. Visualisation : Le Nuage de Points (Scatter Plot)
Le nuage de points est l’outil graphique fondamental pour visualiser la relation entre deux variables quantitatives. Chaque point sur le graphique a pour coordonnées \((x_i, y_i)\). Il nous permet d’observer :
- La forme de la relation : Linéaire, non-linéaire (quadratique, exponentielle…), ou absence de forme.
- Le sens de la relation : Positive (quand X augmente, Y a tendance à augmenter) ou négative (quand X augmente, Y a tendance à diminuer).
- La force de la relation : Les points sont-ils très regroupés autour d’une forme (relation forte) ou très dispersés (relation faible) ?
Exemple : Relation entre l’âge (age) et le tarif du billet (fare) des passagers du Titanic.
2.2 2. Mesures Numériques de la Liaison
La Covariance
La covariance mesure comment deux variables varient conjointement.
Formule (échantillon) : \[Cov(X, Y) = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})\]
Interprétation :
- \(Cov(X, Y) > 0\) : Les variables ont tendance à varier dans le même sens.
- \(Cov(X, Y) < 0\) : Les variables ont tendance à varier en sens opposé.
- \(Cov(X, Y) \approx 0\) : Il n’y a pas de relation linéaire entre les variables.
Limite : Sa valeur dépend des unités de mesure des variables, ce qui la rend difficile à interpréter et à comparer.
Le Coefficient de Corrélation Linéaire de Pearson (r)
Le coefficient de corrélation est une version standardisée de la covariance. Il mesure la force et le sens d’une relation linéaire entre deux variables.
Formule : \[r = \frac{Cov(X, Y)}{s_x s_y} = \frac{\sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_{i=1}^{n} (x_i - \bar{x})^2 \sum_{i=1}^{n} (y_i - \bar{y})^2}}\]
* $r$ est toujours compris entre -1 et 1.
* $r \approx 1$ : Forte corrélation linéaire positive.
* $r \approx -1$ : Forte corrélation linéaire négative.
* $r \approx 0$ : Absence de corrélation **linéaire**.
Une corrélation de 0 ne signifie pas une absence de relation, mais une absence de relation linéaire. De plus, corrélation n’est pas causalité !
# Calcul de la corrélation entre 'age' et 'fare'
correlation = df_titanic[['age', 'fare']].corr()
print(correlation)
# On peut aussi visualiser la matrice de corrélation de plusieurs variables
corr_matrix = df_titanic[['age', 'fare', 'pclass', 'sibsp', 'parch']].corr()
plt.figure(figsize=(8, 6))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt=".2f")
plt.title("Matrice de Corrélation des Variables Numériques")
plt.show()2.3 3. Modélisation : La Droite de Régression des Moindres Carrés
Quand la relation semble linéaire, on peut la modéliser par une droite, appelée droite de régression. L’objectif est de trouver la droite \(\hat{y} = a + bx\) qui “passe au plus près” des points du nuage. La méthode des moindres carrés trouve les coefficients \(a\) (ordonnée à l’origine) et \(b\) (pente) qui minimisent la somme des carrés des erreurs de prédiction (les résidus).
- Pente : \(b = r \frac{s_y}{s_x} = \frac{Cov(X, Y)}{Var(X)}\)
- Ordonnée à l’origine : \(a = \bar{y} - b\bar{x}\)
La fonction regplot de Seaborn fait tout le travail pour nous : elle affiche le nuage de points et la droite de régression estimée, avec un intervalle de confiance.
plt.figure(figsize=(10, 6))
sns.regplot(data=df_titanic, x='age', y='fare', line_kws={"color": "red"})
plt.title("Régression Linéaire : Tarif en fonction de l'Âge")
plt.xlabel("Âge (années)")
plt.ylabel("Tarif ($)")
plt.show()La droite est presque plate, ce qui confirme la très faible corrélation que nous avions calculée.
3 Deux Variables Qualitatives
Lorsqu’on analyse deux variables qualitatives, on cherche à savoir s’il existe une dépendance ou une association entre elles. Autrement dit, est-ce que la répartition des modalités d’une variable change en fonction des modalités de l’autre ?
Exemple : La classe du billet (class) a-t-elle un lien avec la survie (survived) sur le Titanic ?
3.1 1. Le Tableau de Contingence
Le tableau de contingence (ou tableau croisé) est l’outil de base. Il croise les effectifs des deux variables. La fonction pd.crosstab() de Pandas est idéale pour cela.
# Création du tableau de contingence entre 'class' et 'survived'
contingency_table = pd.crosstab(df_titanic['class'], df_titanic['survived'])
print("Tableau de contingence (Effectifs) :")
print(contingency_table)Pour mieux interpréter, on normalise souvent ce tableau par ligne ou par colonne pour obtenir des proportions. Normaliser par ligne nous donnera le taux de survie pour chaque classe.
# Normalisation par ligne pour obtenir les proportions
contingency_table_norm = pd.crosstab(df_titanic['class'], df_titanic['survived'], normalize='index')
print("\nTableau de contingence (Proportions par classe) :")
print(contingency_table_norm.to_markdown(floatfmt=".2%"))On observe immédiatement que le taux de survie (colonne 1) est bien plus élevé en première classe (plus de 60%) qu’en troisième classe (environ 24%).
3.2 2. Visualisation
Diagramme en Barres Groupées
Un diagramme en barres groupées permet de visualiser directement le tableau de contingence. On utilise sns.countplot avec le paramètre hue.
plt.figure(figsize=(10, 6))
sns.countplot(data=df_titanic, x='class', hue='survived', palette='viridis')
plt.title("Répartition des Survivants par Classe de Billet")
plt.xlabel("Classe du Billet")
plt.ylabel("Nombre de Passagers")
plt.legend(title='A survécu', labels=['Non', 'Oui'])
plt.show()Ce graphique montre clairement que bien qu’il y ait plus de passagers en troisième classe, la proportion de survivants y est bien plus faible.
Heatmap
Un heatmap sur le tableau de contingence normalisé est aussi très efficace pour repérer rapidement les associations fortes.
plt.figure(figsize=(8, 5))
sns.heatmap(contingency_table_norm, annot=True, cmap='YlGnBu', fmt='.0%')
plt.title("Taux de Survie par Classe")
plt.xlabel("A survécu (0=Non, 1=Oui)")
plt.ylabel("Classe du Billet")
plt.show()Les cases les plus foncées indiquent les proportions les plus élevées, rendant l’interprétation visuelle immédiate.
3.3 3. Statistique du Chi2 - V de Cramer
Lors de l’analyse de variables qualitatives, on cherche souvent à savoir si un lien existe entre elles. Le test du Chi-carré (\(\chi^2\)) d’indépendance et le V de Cramer sont deux outils statistiques fondamentaux pour répondre à cette question.
- Test du Chi-carré (\(\chi^2\)) : Il permet de tester l’hypothèse nulle (\(H_0\)) selon laquelle deux variables catégorielles sont indépendantes. Un p-value faible (typiquement < 0.05) suggère de rejeter \(H_0\) et indique une association statistiquement significative.
- V de Cramer : Si l’association est significative, le \(\chi^2\) seul ne nous dit pas à quel point les variables sont liées. Le V de Cramer mesure la force de cette association. Sa valeur varie de 0 (aucune association) à 1 (association parfaite).
Formules Mathématiques
Soit un tableau de contingence avec \(r\) lignes et \(k\) colonnes.
Statistique du Chi-carré (\(\chi^2\))
La statistique est calculée en comparant les fréquences observées (\(O_{ij}\)) avec les fréquences attendues (\(E_{ij}\)) sous l’hypothèse d’indépendance.
\[ \chi^2 = \sum_{i=1}^{r} \sum_{j=1}^{k} \frac{(O_{ij} - E_{ij})^2}{E_{ij}} \]
V de Cramer
Le V de Cramer normalise la statistique du \(\chi^2\) pour qu’elle soit comprise entre 0 et 1.
\[V = \sqrt{\frac{\chi^2}{n \times \min(k-1, r-1)}}\]
Où : - \(n\) est le nombre total d’observations. - \(k\) est le nombre de colonnes. - \(r\) est le nombre de lignes.
Example 1 (Exemple d’Application en Python 🐍) Voyons comment tester l’association entre le niveau d’éducation et le sexe dans un jeu de données simulé.
import pandas as pd
import numpy as np
from scipy.stats import chi2_contingency
# 1. Création d'un jeu de données d'exemple
np.random.seed(42)
n = 250
data = pd.DataFrame({
'education': np.random.choice(
['Primaire', 'Secondaire', 'Supérieur'],
n,
p=[0.25, 0.5, 0.25]
),
'sexe': np.random.choice(
['Homme', 'Femme'],
n,
p=[0.5, 0.5]
)
})
# 2. Création du tableau de contingence
contingency_table = pd.crosstab(data['education'], data['sexe'])
print("--- Tableau de Contingence ---")
print(contingency_table)
print("\n" + "="*30 + "\n")
# 3. Réalisation du test du Chi-carré
chi2, p_value, dof, expected = chi2_contingency(contingency_table)
print(f"Statistique du Chi-carré (χ²): {chi2:.4f}")
print(f"P-value: {p_value:.4f}")
print(f"Degrés de liberté (dof): {dof}")
# 4. Interprétation du test du Chi-carré
alpha = 0.05
print("\n--- Interprétation du Test ---")
if p_value < alpha:
print(f"La p-value ({p_value:.4f}) est inférieure à {alpha}.")
print("➡️ On rejette l'hypothèse nulle (H0). Il existe une association statistiquement significative entre le niveau d'éducation et le sexe.")
else:
print(f"La p-value ({p_value:.4f}) est supérieure à {alpha}.")
print("➡️ On ne peut pas rejeter l'hypothèse nulle (H0). Il n'y a pas d'association statistiquement significative.")
# 5. Calcul et Interprétation du V de Cramer
if p_value < alpha:
n_obs = contingency_table.sum().sum()
min_dim = min(contingency_table.shape) - 1
v_cramer = np.sqrt(chi2 / (n_obs * min_dim))
print(f"\nForce de l'association (V de Cramer): {v_cramer:.4f}")
# Guide d'interprétation simple
if v_cramer < 0.1:
strength = "très faible"
elif v_cramer < 0.2:
strength = "faible"
elif v_cramer < 0.3:
strength = "modérée"
else:
strength = "forte"
print(f"➡️ L'intensité de l'association est jugée {strength}.")4 Analyse Bivariée : Quantitative vs Qualitative
L’analyse bivariée entre une variable quantitative et une variable qualitative a pour objectif de comparer la distribution de la variable quantitative à travers les différentes modalités (ou groupes) de la variable qualitative.
On cherche à répondre à des questions comme : - La moyenne du salaire (quantitative) est-elle différente selon le niveau d’éducation (qualitative) ? - La dispersion relative des revenus (quantitative) est-elle la même pour toutes les catégories socio-professionnelles (qualitative) ?
5 Approche Analytique
L’analyse se déroule en deux temps : une comparaison numérique détaillée et une exploration visuelle.
5.1 1. Statistiques Descriptives Détaillées par Groupe
La première étape consiste à calculer un ensemble de statistiques descriptives pour la variable quantitative au sein de chaque groupe. Au-delà de la moyenne et de la médiane, il est crucial d’analyser :
La Dispersion :
- Écart-type (std) : Mesure la dispersion absolue.
- Coefficient de Variation (CV) : Mesure la dispersion relative (\(CV = \frac{\text{écart-type}}{\text{moyenne}}\)). Il est essentiel pour comparer la variabilité entre des groupes ayant des moyennes différentes. Un CV de 0.3 signifie que l’écart-type représente 30% de la moyenne.
La Forme de la Distribution :
- Asymétrie (Skewness) : Indique si la distribution est symétrique ou étalée d’un côté.
- Aplatissement (Kurtosis) : Renseigne sur la concentration des données autour de la moyenne et l’épaisseur des queues de la distribution.
Les Quantiles : Le minimum, le maximum et les quartiles (Q1, Q2/médiane, Q3) donnent un aperçu robuste de la répartition des données.
5.2 2. Visualisations Comparatives
Les graphiques sont indispensables pour visualiser les différences de distribution entre les groupes.
- Boîtes à moustaches (Box Plots) côte à côte : Idéales pour comparer rapidement les médianes, la dispersion (via l’EIQ) et détecter les valeurs aberrantes.
- Diagrammes en violon (Violin Plots) : Plus riches que les box plots, ils montrent également la densité et la forme de la distribution.
Analysons la relation entre le salaire (quantitative) et le niveau d’éducation (qualitative) en calculant un ensemble enrichi de statistiques.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import skew, kurtosis
# 1. Création d'un jeu de données d'exemple
np.random.seed(123)
n = 300
data = pd.DataFrame({
'salaire': np.concatenate([
np.random.normal(30000, 8000, 75), # Salaire Primaire
np.random.normal(45000, 12000, 150), # Salaire Secondaire
np.random.normal(65000, 18000, 75) # Salaire Supérieur
]),
'education': np.repeat(
['Primaire', 'Secondaire', 'Supérieur'],
[75, 150, 75]
)
})
data['education'] = pd.Categorical(data['education'], categories=['Primaire', 'Secondaire', 'Supérieur'], ordered=True)
# 2. Analyse Numérique : Statistiques Détaillées par Groupe
print("--- Statistiques Descriptives Détaillées du Salaire par Niveau d'Éducation ---")
# Définition du coefficient de variation
def cv(x):
return np.std(x) / np.mean(x)
# Agrégation des statistiques
summary_advanced = data.groupby('education')['salaire'].agg(
[
'mean',
'median',
'std',
cv, # Coefficient de variation
skew,
kurtosis,
'min',
lambda x: x.quantile(0.25), # Q1
lambda x: x.quantile(0.75), # Q3
'max'
]
).rename(columns={'<lambda_0>': 'Q1', '<lambda_1>': 'Q3'})
# Affichage du tableau formaté
print(summary_advanced.round(2))
print("\n" + "="*50 + "\n")
# 3. Analyse Visuelle
sns.set_theme(style="whitegrid")
fig, axes = plt.subplots(1, 2, figsize=(16, 7))
fig.suptitle("Analyse Bivariée du Salaire par Niveau d'Éducation", fontsize=16)
# Graphique 1 : Boîtes à Moustaches (Box Plots)
sns.boxplot(ax=axes[0], x='education', y='salaire', data=data, palette="viridis")
axes[0].set_title('Boîtes à Moustaches Comparatives')
axes[0].set_xlabel("Niveau d'Éducation")
axes[0].set_ylabel('Salaire (€)')
# Graphique 2 : Diagrammes en Violon (Violin Plots)
sns.violinplot(ax=axes[1], x='education', y='salaire', data=data, palette="plasma")
axes[1].set_title('Diagrammes en Violon Comparatifs')
axes[1].set_xlabel("Niveau d'Éducation")
axes[1].set_ylabel('Salaire (€)')
plt.tight_layout(rect=[0, 0, 1, 0.96])
plt.show()