tips
Dans cette séance, nous allons utiliser les données tips
,
contenant des informations recueillies par un serveur pendant son
travail. Les voici présentées ci-dessous (les 6 premières lignes).
library(tidyverse)
library(DT)
tips = read_csv("https://fxjollois.github.io/cours-2024-2025/intechmer-3a-add-tests/tips.csv")
datatable(tips)
Lorsqu’on réalise une description des variables quantitatives, une des questions à se poser est la normalité de la variable. Dans le cas de données, puisque c’est la variable d’intérêt, on peut se poser la question suivante :
La variable
total_bill
suit-elle une loi normale ?
Premier graphique à créer, l’histogramme permet d’avoir un premier élément de réponse.
La distribution semble unimodale (un seul pic– en diminuant le nombre d’intervalles) mais pas totale symétrique.
ggplot(tips, aes(total_bill)) +
geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
ggplot(tips, aes(total_bill)) +
geom_histogram(bins = 15)
Pour avoir une meilleure estimation de la distribution de la variable, il est d’usage de représenter une estimation par noyau de la densité, comme ci-dessous. L’intérêt est que cette représentation est moins sensible qu’un histogramme, dont l’allure peut changer en fonction du nombre d’intervalles choisi.
On fait la même observation que ci-dessus.
ggplot(tips, aes(total_bill)) +
geom_histogram(aes(y = after_stat(density)), bins = 15, fill = "lightgray") +
geom_density()
Mais le meilleur moyen est de réaliser ce qu’on appelle un Q-Q plot. Ce graphique présente les valeurs observées (en \(Y\)) pour les quantiles, versus les valeurs théoriques d’une loi Normale. Si les points suivent la droite, il y a normalité, sinon, on s’en éloigne.
On remarque que la variable
total_bill
ne suit pas la droite.
ggplot(tips, aes(sample = total_bill)) +
stat_qq() + # représentation des quantiles observés
stat_qq_line() # affichage de la droite, représentant la "normalité"
La question peut se résoudre formellement à l’aide d’un test statistique, ici le test de Shapiro-Wilk. Celui-ci oppose deux hypothèses :
Pour tous les tests que nous verrons par la suite, le fonctionnement d’un test est le suivant :
shapiro.test()
) calculera la
probabilité d’être dans l’hypothèse \(H_0\) au vu de ce qui est observé. Cette
probabilité, \(p(H_0)\), est appelé
p-value ;Voici donc son utilisation sur le montant total de la facture.
On en conlue que la variable ne suit pas une loi normale (la p-value étant largement inférieure à 0.001).
shapiro.test(tips$total_bill)
##
## Shapiro-Wilk normality test
##
## data: tips$total_bill
## W = 0.91972, p-value = 3.325e-10
Tester la normalité de la variable tip
, en produisant
les graphiques nécessaires.
Dans la suite, nous allons voir deux cas spécifiques de comparaison, pour lesquels le test utilisé est le même :
Dans ces deux cas, deux tests seront possibles :
t.test()
);wilcox.test()
)
L’usage de l’un ou l’autre test dépend donc de la normalité des variables. Cependant, le Théorème Central Limite (TCL, qui affine la loi des grands nombres) permet de justifier l’usage du premier dès que \(n\) est grand.
Ce cas est utilisée dès qu’on a une idée de la moyenne que doit prendre la variable. Par exemple, cela peut être utile lorsqu’on souhaite vérifier qu’un échantillon est représentatif par exemple.
Imaginons que notre serveur pense que le montant total est de 18$ en moyenne (parce qu’on lui a dit cela par exemple). Nous allons utiliser les deux tests pour se décider. Dans les deux fonctions, les hypothèses par défaut sont les suivantes :
Il est possible de définir deux éléments :
mu
;alternative
:
"two-sided"
) \(\rightarrow\) on teste juste la différence
;"less"
) \(\rightarrow\) valeur observée inférieure à
la référence ;"greater"
) \(\rightarrow\) valeur observée supérieure à
la référence.Si l’on suit le résultat fourni par le \(t\)-test, avec un risque d’erreur à 5%, nous conluons que le montant moyen est différent de 18. Alors qu’avec le test de Wilcoxon, nous ne pouvons pas rejeter l’hypothèse nulle, et donc concluons qu’il n’y a pas de différence.
t.test(tips$total_bill, mu = 18)
##
## One Sample t-test
##
## data: tips$total_bill
## t = 3.1337, df = 243, p-value = 0.001938
## alternative hypothesis: true mean is not equal to 18
## 95 percent confidence interval:
## 18.66333 20.90855
## sample estimates:
## mean of x
## 19.78594
wilcox.test(tips$total_bill, mu = 18)
##
## Wilcoxon signed rank test with continuity correction
##
## data: tips$total_bill
## V = 16424, p-value = 0.1802
## alternative hypothesis: true location is not equal to 18
Si l’on change l’hypothèse alternative, en cherchant à savoir si la valeur est supérieure à 18, comme le laisse penser la moyenne calculée, nous obtenons les conclusions suivantes :
t.test(tips$total_bill, mu = 18, alternative = "greater")
##
## One Sample t-test
##
## data: tips$total_bill
## t = 3.1337, df = 243, p-value = 0.0009692
## alternative hypothesis: true mean is greater than 18
## 95 percent confidence interval:
## 18.84492 Inf
## sample estimates:
## mean of x
## 19.78594
wilcox.test(tips$total_bill, mu = 18, alternative = "greater")
##
## Wilcoxon signed rank test with continuity correction
##
## data: tips$total_bill
## V = 16424, p-value = 0.0901
## alternative hypothesis: true location is greater than 18
Ce cas est intéressant quand on a deux échantillons de à comparer (deux études, deux groupes de personnes…). Dans notre cas, on peut se demander si le montant payé est le même selon que ce soit un homme ou une femme qui paie l’addition.
Ici, les hypothèses seront les suivantes :
Comme précédemment, il est possible de changer l’hypothèse alternative pour chercher si le deuxième échantillon a une valeur plus petite ou plus grande que le premier.
La première étape à faire est de comparer numériquement et visuellement les deux distributions. Cela peut se faire soit via des histogrammes, soit via des estimation de densités, soit via des boîtes à moustaches.
Nous remarquons que les montants payés lorsque c’est un homme sont plus élevés que lorsque c’est une femme qui règle l’addition.
tips %>%
group_by(sex) %>%
summarise(Moyenne = mean(total_bill))
## # A tibble: 2 × 2
## sex Moyenne
## <chr> <dbl>
## 1 Female 18.1
## 2 Male 20.7
ggplot(tips, aes(x = total_bill, fill = sex)) +
geom_histogram(bins = 15) +
facet_wrap(~ sex, ncol = 1)
ggplot(tips, aes(x = total_bill, col = sex)) +
geom_density()
ggplot(tips, aes(x = total_bill, fill = sex)) +
geom_boxplot()
Pour réaliser les tests, nous utilisons donc les mêmes fonctions,
avec une notation spécifique, de type formulae
, qui permet
de bien comprendre ce qu’on cherche à tester.
Dans les deux tests, la conclusion sera la même : avec un seuil d’erreur 5%, nous conluons que les montants payés par les hommes sont plus élevés que ceux payés par les femmes.
t.test(data = tips, total_bill ~ sex)
##
## Welch Two Sample t-test
##
## data: total_bill by sex
## t = -2.3734, df = 199.85, p-value = 0.01857
## alternative hypothesis: true difference in means between group Female and group Male is not equal to 0
## 95 percent confidence interval:
## -4.919787 -0.454573
## sample estimates:
## mean in group Female mean in group Male
## 18.05690 20.74408
wilcox.test(data = tips, total_bill ~ sex)
##
## Wilcoxon rank sum test with continuity correction
##
## data: total_bill by sex
## W = 5613.5, p-value = 0.02135
## alternative hypothesis: true location shift is not equal to 0
Tester les hypothèses suivantes (avec les graphiques pour justifier) :
tip
) est le même selon le sexe du
payeur ;smoker
) ;time
).Ici, nous verrons comment répondre à deux comparaisons, en utilisant
la même fonction chisq.test()
pour réaliser un test
du \(\chi^2\) :
Dans ce cadre, nous cherchons à savoir si les modalités d’une
variable sont distribuées de façon spécifique, par rapport à une
référence. Par défaut, on cherche à savoir s’il y a équi-probabilité
(toutes les modalités ayant la même probabilité d’apparition). Sinon,
avec le paramètre p
, nous pouvons définir une répartition
connue (par exemple en cas de questionnement sur la représentativité).
On peut chercher à savoir s’il y a autant d’hommes que de femmes qui
règlent l’addition.
Dans ce cas, les hypothèses sont les suivantes
Comme d’habitude, nous devons déjà représenter la distribution, via un tableau et un diagramme en barres par exemple.
On remarque un fort déséquilibre entre les deux sexes.
t = table(tips$sex)
t
##
## Female Male
## 87 157
prop.table(t)
##
## Female Male
## 0.3565574 0.6434426
ggplot(tips, aes(y = sex, fill = sex)) +
geom_bar()
Avec un seuil de 5%, il est très clair qu’il n’y a pas équi-probabilité des deux sexes.
chisq.test(t)
##
## Chi-squared test for given probabilities
##
## data: t
## X-squared = 20.082, df = 1, p-value = 7.419e-06
Comme précédemment, on peut vouloir comparer à une répartition de
référence, comme par exemple 30% de femmes et 70% d’hommes. Les
probabilités de référence s’écrivent dans l’ordre alphabétique des
modalités (ici, Female
, puis Male
).
Au seuil de 5%, nous ne conluons pas à une différence entre ce qui est observé et ce qui est prévu (notre 30%/70%).
chisq.test(t, p = c(.3, .7))
##
## Chi-squared test for given probabilities
##
## data: t
## X-squared = 3.7166, df = 1, p-value = 0.05387
Dans ce cadre, nous souhaitons savoir si deux variables qualitatives sont liées. Pour cela, nous comparons la table de contingence (croisement des modalités des deux variables) observée à celle estimée s’il y avait indépendance entre les variables. Nous pouvons nous demander ici s’il y a un lien entre l’heure du repas et le sexe de la personne qui a payé.
Ici, les hypothèses sont les suivantes
Encore une fois, on décrit déjà la relation entre ces deux variables de façon numérique et graphique.
Analyser la table de contingence directement est assez complexe, d’autant plus si le nombre de modalités est grand. Ici, on peut envisager de la lire directement et voir que pour le déjeuner, c’est équilibré entre homme et femme, au contraire du dîner.
tt = table(tips$time, tips$sex)
tt
##
## Female Male
## Dinner 52 124
## Lunch 35 33
Pour mieux comprendre la relation entre les deux, il est d’usage de
présenter les profils lignes ou les profils colonnes (en fonction des
phrases que l’on souhaite écrire/dire). La fonction
prop.table()
, en plus de calculer les proportions globales,
permet de réaliser ces profils lignes (margin = 1
) ou ces
profils colonnes (margin = 2
).
Dans les deux cas, on observe le même lien entre les deux modalités. Mais ici, on aura tendance à prendre les profils lignes.
round(prop.table(tt, margin = 1), 2)
##
## Female Male
## Dinner 0.30 0.70
## Lunch 0.51 0.49
round(prop.table(tt, margin = 2), 2)
##
## Female Male
## Dinner 0.60 0.79
## Lunch 0.40 0.21
Graphiquement, il est préférable de réaliser des diagrammes en barres empilées (idem, on a 2 versions possibles).
Idem, ici, le premier graphique semble plus facile à présenter.
ggplot(tips, aes(time, fill = sex)) +
geom_bar(position = "fill")
ggplot(tips, aes(sex, fill = time)) +
geom_bar(position = "fill")
La p-value étant inférieure à 5 %, on en conclue qu’il y a bien un lien entre les deux variables.
chisq.test(tt)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tt
## X-squared = 9.3438, df = 1, p-value = 0.002237
Tester les liens suivants (avec les graphiques pour justifier) :
En présence de deux variables quantitatives, il est courant de calculer le coefficient de corrélation linéaire entre celles-ci pour savoir s’il existe un lien linéaire entre les deux. Pour rappel, ce coefficient a une valeur entre -1 et 1, qui se lit comme suit :
Dans nos données, on peut vouloir chercher s’il y a un lien linéaire entre le montant total payé et le pourboire donné.
Dans les tests de corrélation, les hypothèses sont les suivantes
Comme pour le \(t\)-test, il existe
une version paramétrique (dit de Pearson) et deux
versions non-paramétriques (Keandall et
Spearman), ici aussi basées sur les rangs. Toutes trois
sont calculées avec la même fonction cor.test()
(le
paramètre method
permettant de choisir le test en donnant
son nom en minuscule). Par défaut, c’est celui de Pearson qui est
calculé.
On peut aussi modifier l’hypothèse alternative (de façon similaire au
\(t\)-test encore), toujours avec le
paramètre alternative
.
On va déjà calculer la corrélation entre les deux variable (avec les trois méthodes) et réaliser un nuage de points pour observer s’il y a une relation entre celles-ci.
On observe un lien positif entre les deux variables, ce qui semble logique.
print(cor(tips$total_bill, tips$tip)) # Pearson
## [1] 0.6757341
print(cor(tips$total_bill, tips$tip, method = "kendall"))
## [1] 0.517181
print(cor(tips$total_bill, tips$tip, method = "spearman"))
## [1] 0.6789681
ggplot(tips, aes(total_bill, tip)) +
geom_point()
Nos trois tests nous mènent à la même conclusion : il y a bien un lien linéaire entre les deux variables.
cor.test(tips$total_bill, tips$tip)
##
## Pearson's product-moment correlation
##
## data: tips$total_bill and tips$tip
## t = 14.26, df = 242, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.6011647 0.7386372
## sample estimates:
## cor
## 0.6757341
cor.test(tips$total_bill, tips$tip, method = "kendall")
##
## Kendall's rank correlation tau
##
## data: tips$total_bill and tips$tip
## z = 11.839, p-value < 2.2e-16
## alternative hypothesis: true tau is not equal to 0
## sample estimates:
## tau
## 0.517181
cor.test(tips$total_bill, tips$tip, method = "spearman")
## Warning in cor.test.default(tips$total_bill, tips$tip, method = "spearman"):
## Cannot compute exact p-value with ties
##
## Spearman's rank correlation rho
##
## data: tips$total_bill and tips$tip
## S = 777247, p-value < 2.2e-16
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.6789681
Tester les liens suivants (avec les graphiques pour justifier) :
size
) ;Quel commentaire pouvons-nous faire sur ces deux derniers tests ?
Nous allons utiliser le jeu de données adult.csv
,
issu d’une enquête aux USA pour analyser le salaire (variable binaire :
plus ou moins de 50k$) en fonction de certains critères (âge, sexe,
éducation, nombre d’heures de travail par semaine…).
adult = read_csv("https://fxjollois.github.io/cours-2024-2025/intechmer-3a-add-tests/adult.csv")
## Rows: 32561 Columns: 15
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (9): workclass, education, marital_status, occupation, relationship, rac...
## dbl (6): age, fnlwgt, education_num, capital_gain, capital_loss, hours_per_week
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
datatable(adult, options = list(scrollX = TRUE))
## Warning in instance$preRenderHook(instance): It seems your data is too big for
## client-side DataTables. You may consider server-side processing:
## https://rstudio.github.io/DT/server.html
Tester les éléments suivants :
hours_per_week
)class
)Identifier si les liens suivants sont réels :
race
) et salaire