Aller au contenu

Comparez deux échantillons

Une équipe web a réécrit une page de son site. La nouvelle version se charge-t-elle plus vite que l’ancienne ? Des patients traités guérissent-ils plus vite que des patients sous placebo ? Les abonnés venus des réseaux sociaux choisissent-ils la même formule que les autres ?

Toutes ces questions comparent deux groupes. On ne teste plus un paramètre contre une valeur fixée, mais deux paramètres l’un contre l’autre. Ce chapitre présente les tests correspondants, dans l’ordre où on les enchaîne en pratique, sur un fil rouge de temps de chargement.

On mesure le temps de chargement (en secondes) de l’ancienne version A sur 10 visites et de la nouvelle version B sur 8 autres visites :

VersionTemps de chargement (s)
A (ancienne)2,8 ; 3,4 ; 3,1 ; 2,6 ; 3,9 ; 3,3 ; 2,9 ; 3,6 ; 3,0 ; 3,4
B (nouvelle)2,5 ; 2,9 ; 2,4 ; 3,1 ; 2,7 ; 2,2 ; 2,8 ; 2,6

Résumés : n1=10n_1 = 10, xˉ=3,20\bar x = 3{,}20 s, s~X2≈0,156\tilde s_X^2 \approx 0{,}156 ; n2=8n_2 = 8, yˉ=2,65\bar y = 2{,}65 s, s~Y2≈0,083\tilde s_Y^2 \approx 0{,}083.

On suppose les deux échantillons indépendants (des visites différentes), gaussiens : Xi∼N(mX,σX2)X_i \sim \mathcal{N}(m_X, \sigma_X^2) et Yj∼N(mY,σY2)Y_j \sim \mathcal{N}(m_Y, \sigma_Y^2). La version B a l’air plus rapide de 0,55 s. Est-ce significatif ?

2,02,53,03,54,0temps de chargement (s)x̄ = 3,20 sA (ancienne)ȳ = 2,65 sB (nouvelle)0,55 s
Les deux nuages se chevauchent en partie : l'écart de 0,55 s entre les moyennes est-il plus grand que ce que le hasard des visites produirait ? C'est la question du test.

Sous ces hypothèses, deux résultats servent de base à tout le chapitre :

(Xˉ−Yˉ)−(mX−mY)σX2/n1+σY2/n2∼N(0,1)etS~X2/σX2S~Y2/σY2∼Fn1−1,n2−1\frac{(\bar X - \bar Y) - (m_X - m_Y)}{\sqrt{\sigma_X^2/n_1 + \sigma_Y^2/n_2}} \sim \mathcal{N}(0, 1) \qquad \text{et} \qquad \frac{\tilde S_X^2/\sigma_X^2}{\tilde S_Y^2/\sigma_Y^2} \sim \mathcal{F}_{n_1 - 1, n_2 - 1}

Le premier vient de ce qu’une différence de normales indépendantes est normale ; le second, du théorème de Fisher appliqué à chaque échantillon et de la définition de la loi de Fisher.

Étape 1 : comparer les variances avec le test de Fisher

Section intitulée « Étape 1 : comparer les variances avec le test de Fisher »

Pourquoi commencer par les variances ? Parce que le test de comparaison des moyennes le plus simple suppose des variances égales. On teste donc d’abord H0:σX2=σY2H_0 : \sigma_X^2 = \sigma_Y^2 contre H1:σX2≠σY2H_1 : \sigma_X^2 \ne \sigma_Y^2.

Sous H0H_0, les σ2\sigma^2 se simplifient dans le second résultat :

F0=S~X2S~Y2∼Fn1−1,n2−1F_0 = \frac{\tilde S_X^2}{\tilde S_Y^2} \sim \mathcal{F}_{n_1 - 1, n_2 - 1}

On rejette H0H_0 si F0F_0 est trop petit ou trop grand, c’est-à-dire hors de [fn1−1,n2−1;α/2 ; fn1−1,n2−1;1−α/2][f_{n_1-1,n_2-1;\alpha/2} \ ;\ f_{n_1-1,n_2-1;1-\alpha/2}]. En pratique, on place la plus grande des deux variances au numérateur : F0≥1F_0 \ge 1, et il suffit de comparer au quantile supérieur f1−α/2f_{1-\alpha/2}. C’est possible grâce à la relation fk,l;α/2=1/fl,k;1−α/2f_{k,l;\alpha/2} = 1/f_{l,k;1-\alpha/2}.

012345678rapport des variances0,00,10,20,30,40,50,60,7densitéseuil f₉,₇ ; 0,975 = 4,82F₀ = 0,156 / 0,083 ≈ 1,88loi de Fisher à (9, 7) ddl sous H₀
Un rapport de variances de 1,88 est courant pour des échantillons aussi petits : sous H₀, il faudrait dépasser 4,82 pour conclure que les variances diffèrent.

Étape 2 : comparer les moyennes avec le test de Student

Section intitulée « Étape 2 : comparer les moyennes avec le test de Student »

Si σX2=σY2=σ2\sigma_X^2 = \sigma_Y^2 = \sigma^2, on estime cette variance commune en regroupant les deux échantillons. C’est la variance poolée :

S~p2=(n1−1) S~X2+(n2−1) S~Y2n1+n2−2\tilde S_p^2 = \frac{(n_1 - 1)\,\tilde S_X^2 + (n_2 - 1)\,\tilde S_Y^2}{n_1 + n_2 - 2}

Chaque échantillon y pèse selon son nombre de degrés de liberté. Sous H0:mX=mYH_0 : m_X = m_Y,

T0=Xˉ−YˉS~p1n1+1n2∼Tn1+n2−2T_0 = \frac{\bar X - \bar Y}{\tilde S_p \sqrt{\dfrac{1}{n_1} + \dfrac{1}{n_2}}} \sim \mathcal{T}_{n_1 + n_2 - 2}

Les régions critiques sont celles du test de Student à un échantillon, avec n1+n2−2n_1 + n_2 - 2 degrés de liberté.

−4−3−2−101234valeur de T₀0,00,10,20,30,4densitéseuil unilatéral 1,746 (5 %)t₀ = 3,30p ≈ 0,002Student à 16 ddlsous H₀
t₀ = 3,30 tombe loin derrière le seuil 1,746 : sous H₀, un écart aussi favorable à B n'arriverait que 2 fois sur 1 000.

Si les variances diffèrent, la statistique

T0=Xˉ−YˉS~X2/n1+S~Y2/n2T_0 = \frac{\bar X - \bar Y}{\sqrt{\tilde S_X^2/n_1 + \tilde S_Y^2/n_2}}

n’a plus de loi exacte simple. Pour de grands échantillons, le TCL assure qu’elle suit approximativement N(0,1)\mathcal{N}(0, 1) sous H0H_0, même sans normalité : c’est le test asymptotique vu en cours. Pour de petits échantillons gaussiens, Welch a proposé de l’approcher par une loi de Student dont le nombre de degrés de liberté se calcule à partir des données (formule de Welch-Satterthwaite). C’est le comportement par défaut de t.test en R.

Sur nos données, le test de Welch donne t0≈3,42t_0 \approx 3{,}42 avec environ 15,9 degrés de liberté, et une p-valeur unilatérale d’environ 0,002 : même conclusion.

A <- c(2.8, 3.4, 3.1, 2.6, 3.9, 3.3, 2.9, 3.6, 3.0, 3.4)
B <- c(2.5, 2.9, 2.4, 3.1, 2.7, 2.2, 2.8, 2.6)
var.test(A, B) # Fisher : F = 1.88, p = 0.42
t.test(A, B, var.equal = TRUE, alternative = "greater") # Student : t = 3.30, p = 0.002
t.test(A, B, alternative = "greater") # Welch (défaut de R)
from scipy import stats
A = [2.8, 3.4, 3.1, 2.6, 3.9, 3.3, 2.9, 3.6, 3.0, 3.4]
B = [2.5, 2.9, 2.4, 3.1, 2.7, 2.2, 2.8, 2.6]
print(stats.ttest_ind(A, B, equal_var=True, alternative="greater")) # Student
print(stats.ttest_ind(A, B, equal_var=False, alternative="greater")) # Welch

Le même raisonnement vaut pour deux proportions. Sur n1n_1 visiteurs de la version A, p^1\hat p_1 ont acheté ; sur n2n_2 de la version B, p^2\hat p_2. Sous H0:p1=p2H_0 : p_1 = p_2, on estime la proportion commune par p^=(n1p^1+n2p^2)/(n1+n2)\hat p = (n_1\hat p_1 + n_2\hat p_2)/(n_1 + n_2), et

T0=p^1−p^2p^(1−p^)(1n1+1n2)≈N(0,1)T_0 = \frac{\hat p_1 - \hat p_2}{\sqrt{\hat p(1-\hat p)\left(\dfrac{1}{n_1} + \dfrac{1}{n_2}\right)}} \approx \mathcal{N}(0, 1)

C’est le test des tests A/B sur les taux de conversion (prop.test(c(x1, x2), c(n1, n2)) en R). Il est équivalent au test du chi-deux d’indépendance sur un tableau 2×22 \times 2, que nous voyons maintenant.

Complément : le test d’indépendance du chi-deux

Section intitulée « Complément : le test d’indépendance du chi-deux »

Nos supports de cours ne présentent pas ce test ; c’est un complément standard, très utilisé en analyse de données. Il répond à la question : deux variables qualitatives sont-elles liées ?

Un service de streaming croise la formule choisie par 160 nouveaux abonnés et le canal par lequel ils l’ont découvert :

ObservéRéseaux sociauxMoteur de rechercheBouche-à-oreilleTotal
Mensuel483022100
Annuel12202860
Total605050160

On teste H0H_0 : « la formule est indépendante du canal » contre H1H_1 : « elles sont liées ». Sous H0H_0, la probabilité d’une case est le produit des probabilités marginales, d’où l’effectif attendu

Eij=ni⋅×n⋅jnE_{ij} = \frac{n_{i\cdot} \times n_{\cdot j}}{n}

où ni⋅n_{i\cdot} est le total de la ligne ii, n⋅jn_{\cdot j} celui de la colonne jj et nn l’effectif total. Par exemple, E11=100×60/160=37,5E_{11} = 100 \times 60/160 = 37{,}5. La statistique

D=∑i,j(Oij−Eij)2Eij≈χ(I−1)(J−1)2 sous H0D = \sum_{i,j} \frac{(O_{ij} - E_{ij})^2}{E_{ij}} \approx \chi^2_{(I-1)(J-1)} \text{ sous } H_0

compare observé et attendu, pour un tableau à II lignes et JJ colonnes. Le nombre de degrés de liberté, (I−1)(J−1)(I-1)(J-1), correspond au nombre de cases qu’on peut remplir librement une fois les marges fixées.

025 %50 %75 %100 %Réseaux sociaux(n = 60)Moteur de recherche(n = 50)Bouche-à-oreille(n = 50)mensuel 80 %annuel 20 %contributions2,94 + 4,90mensuel 60 %annuel 40 %contributions0,05 + 0,08mensuel 44 %annuel 56 %contributions2,74 + 4,56ensemble : 62,5 % mensuel
Sous l'indépendance, les trois barres seraient coupées au même endroit (62,5 %). Les réseaux sociaux et le bouche-à-oreille s'en écartent le plus : ce sont eux qui portent les grosses contributions.
tab <- matrix(c(48, 12, 30, 20, 22, 28), nrow = 2,
dimnames = list(formule = c("mensuel", "annuel"),
canal = c("reseaux", "moteur", "bouche")))
test <- chisq.test(tab)
test # X-squared = 15.27, df = 2, p-value = 0.00048
test$expected # effectifs attendus sous H0
from scipy import stats
tab = [[48, 30, 22], [12, 20, 28]]
chi2, p, ddl, attendus = stats.chi2_contingency(tab)
print(chi2, p, ddl) # 15.27, 0.00048, 2
  • Deux échantillons indépendants et gaussiens : Fisher pour les variances, S~X2/S~Y2∼Fn1−1,n2−1\tilde S_X^2/\tilde S_Y^2 \sim \mathcal{F}_{n_1-1,n_2-1}, la plus grande au numérateur.
  • Student à variances égales avec la variance poolée S~p2\tilde S_p^2, et n1+n2−2n_1 + n_2 - 2 degrés de liberté ; Welch si les variances diffèrent ; test asymptotique normal pour de grands échantillons.
  • Des données appariées se traitent par un test à un échantillon sur les différences.
  • Deux proportions : test normal avec proportion commune estimée, base des tests A/B.
  • Indépendance du chi-deux (complément) : Eij=ni⋅n⋅j/nE_{ij} = n_{i\cdot} n_{\cdot j}/n, D≈χ(I−1)(J−1)2D \approx \chi^2_{(I-1)(J-1)} ; les contributions montrent d’où vient le lien.

Exercice · Enchaîner Fisher et Student

facile

Deux engrais sont testés sur des parcelles indépendantes. Engrais 1 : n1=12n_1 = 12, xˉ=52,0\bar x = 52{,}0 quintaux par hectare, s~X2=16\tilde s_X^2 = 16. Engrais 2 : n2=12n_2 = 12, yˉ=48,5\bar y = 48{,}5, s~Y2=20\tilde s_Y^2 = 20. On suppose la normalité. Au niveau 5 %, les rendements moyens diffèrent-ils ? (f11,11;0,975≈3,47f_{11,11;0,975} \approx 3{,}47 ; t22;0,975≈2,074t_{22;0,975} \approx 2{,}074.)

Voir la solution

Variances. On met la plus grande au numérateur : F0=20/16=1,25<3,47F_0 = 20/16 = 1{,}25 < 3{,}47. On conserve l’égalité des variances.

Moyennes. s~p2=(11×16+11×20)/22=18\tilde s_p^2 = (11 \times 16 + 11 \times 20)/22 = 18, donc s~p≈4,243\tilde s_p \approx 4{,}243.

t0=52,0−48,54,2431/12+1/12=3,54,243×0,408≈2,02t_0 = \frac{52{,}0 - 48{,}5}{4{,}243\sqrt{1/12 + 1/12}} = \frac{3{,}5}{4{,}243 \times 0{,}408} \approx 2{,}02

Comme ∣t0∣=2,02<2,074\lvert t_0 \rvert = 2{,}02 < 2{,}074, on conserve H0H_0 au niveau 5 % : la différence n’est pas significative, de justesse (p-valeur ≈0,056\approx 0{,}056). Une expérience avec plus de parcelles serait utile.

En code

import numpy as np
from scipy import stats
n1, x_bar, s2_x = 12, 52.0, 16
n2, y_bar, s2_y = 12, 48.5, 20
# Fisher : plus grande variance au numérateur
F0 = s2_y / s2_x
print(F0, stats.f.ppf(0.975, n2 - 1, n1 - 1)) # 1.25 < 3.47 : variances compatibles
# Student à variance poolée, à partir des résumés
res = stats.ttest_ind_from_stats(x_bar, np.sqrt(s2_x), n1, y_bar, np.sqrt(s2_y), n2,
equal_var=True)
print(res.statistic, res.pvalue) # 2.02 0.056 : on conserve H0

Exercice · Un test A/B sur un taux de conversion

moyen

Version A : 120 achats sur 2 400 visiteurs. Version B : 156 achats sur 2 400 visiteurs. Au niveau 5 %, B convertit-elle mieux ?

Voir la solution

p^1=0,050\hat p_1 = 0{,}050 et p^2=0,065\hat p_2 = 0{,}065. On teste H0:p2≤p1H_0 : p_2 \le p_1 contre H1:p2>p1H_1 : p_2 > p_1. Proportion commune : p^=276/4 800=0,0575\hat p = 276/4\,800 = 0{,}0575.

t0=0,065−0,0500,0575×0,9425×(2/2 400)=0,0150,00672≈2,23t_0 = \frac{0{,}065 - 0{,}050}{\sqrt{0{,}0575 \times 0{,}9425 \times (2/2\,400)}} = \frac{0{,}015}{0{,}00672} \approx 2{,}23

Comme 2,23>1,6452{,}23 > 1{,}645, on rejette H0H_0 : B convertit significativement mieux (p-valeur ≈0,013\approx 0{,}013). Attention toutefois : ce calcul n’est valide que si la taille d’échantillon a été fixée à l’avance, et non en arrêtant l’expérience au premier résultat favorable.

En code

import numpy as np
from scipy import stats
from statsmodels.stats.proportion import proportions_ztest
achats = np.array([156, 120]) # B puis A
visiteurs = np.array([2400, 2400])
# À la main, avec la proportion commune
p1, p2 = achats / visiteurs
p = achats.sum() / visiteurs.sum() # 0.0575
t0 = (p1 - p2) / np.sqrt(p * (1 - p) * (1 / 2400 + 1 / 2400))
print(t0, stats.norm.sf(t0)) # 2.23 0.013
# Même test avec statsmodels (H1 : p_B > p_A)
print(proportions_ztest(achats, visiteurs, alternative="larger")) # (2.23, 0.013)

Exercice · Indépendance sur un tableau 2 × 2

difficile

Sur 200 patients, 100 reçoivent un traitement et 100 un placebo. Guérisons : 70 sous traitement, 55 sous placebo. Calculez la statistique du chi-deux d’indépendance (sans correction) et vérifiez qu’elle vaut le carré de la statistique du test de comparaison de deux proportions.

Voir la solution

Tableau : traitement (70 guéris, 30 non), placebo (55, 45). Marges : 125 guéris, 75 non guéris, 100 par groupe.

Effectifs attendus : 100×125/200=62,5100 \times 125/200 = 62{,}5 guéris et 37,537{,}5 non guéris dans chaque groupe. Chaque écart vaut ±7,5\pm 7{,}5 :

D=2×7,5262,5+2×7,5237,5=1,8+3,0=4,8D = 2 \times \frac{7{,}5^2}{62{,}5} + 2 \times \frac{7{,}5^2}{37{,}5} = 1{,}8 + 3{,}0 = 4{,}8

Avec (2−1)(2−1)=1(2-1)(2-1) = 1 degré de liberté, on compare à χ1;0,952≈3,84\chi^2_{1;0,95} \approx 3{,}84 : on rejette l’indépendance.

Test de deux proportions : p^1=0,70\hat p_1 = 0{,}70, p^2=0,55\hat p_2 = 0{,}55, p^=0,625\hat p = 0{,}625. t0=0,15/0,625×0,375×0,02=0,15/0,0685≈2,19t_0 = 0{,}15/\sqrt{0{,}625 \times 0{,}375 \times 0{,}02} = 0{,}15/0{,}0685 \approx 2{,}19, et 2,192≈4,8=D2{,}19^2 \approx 4{,}8 = D. De même, 1,962≈3,841{,}96^2 \approx 3{,}84 : les deux tests bilatéraux sont identiques.

En code

import numpy as np
from scipy import stats
from statsmodels.stats.proportion import proportions_ztest
tab = np.array([[70, 30], # traitement : guéris, non guéris
[55, 45]]) # placebo
D, p, ddl, attendus = stats.chi2_contingency(tab, correction=False)
print(attendus) # [[62.5 37.5] [62.5 37.5]]
print(D, ddl, p) # 4.8 1 0.028
z, p_z = proportions_ztest([70, 55], [100, 100])
print(z, z**2, p_z) # 2.19 4.8 0.028 : même test