20 Atelier RStudio : Analyse de la variance bidirectionnelle
Analyse de la variance bidirectionnelle intraparticipant
La même fonction peut servir au calcul d’une analyse de la variance unidirectionnelle intrasujet et d’une analyse de la variance bidirectionnelle. Je vous encourage fortement à tenter d’effectuer les analyses avant de poursuivre la lecture de ce guide.
Pour cet exercice, nous allons examiner le fichier de données intitulé « Practice_2WayANOVA_Within.xlsx ».
- Variable mesurée : Résultat du test
- Plan à mesures répétées 2 x 3
- Facteurs : « Boisson » et « Sommeil »
Dans cette expérience hypothétique, les participants ont assisté à six conférences à différents moments. Il leur a été demandé d’assister à une conférence, puis de revenir le lendemain pour répondre à un questionnaire à ce sujet après avoir dormi 0 heure, 5 heures ou 8 heures. Au moment du test, ils ont reçu un café ou un placebo (boisson chaude décaféinée).
Nous cherchons à savoir si les résultats des tests diminuent lorsque les participants ne dorment pas suffisamment et si la consommation de café atténue cette baisse de performance.
mydata2w <-read_excel("Practice_2WayANOVA_Within.xlsx") #importing our data file
mydata2w <- mydata2w %>%
convert_as_factor(ID, Gender, Beverage, Sleep) #setting up factors
str(mydata2w) #checking factors
## tibble [516 × 6] (S3: tbl_df/tbl/data.frame)
## $ ID : Factor w/ 86 levels "29057","32239",..: 20 48 84 61 22 23 25 29 72 69 ...
## $ Gender : Factor w/ 3 levels "F","M","Non-Binary": 1 2 1 1 1 1 1 2 1 1 ...
## $ Age : num [1:516] 25 22 22 20 22 20 21 20 19 21 ...
## $ Beverage : Factor w/ 2 levels "Coffee","Placebo": 1 1 1 1 1 1 1 1 1 1 ...
## $ Sleep : Factor w/ 3 levels "0","5","8": 1 1 1 1 1 1 1 1 1 1 ...
## $ TestScore: num [1:516] 99 64.3 93.3 94.8 89.1 ...
# Summary Statistics Table
mydata2w.summarystat <- mydata2w %>%
group_by(Beverage, Sleep) %>%
get_summary_stats(TestScore, type = "full")
# Selecting rows of data only if ID value is unique
mydata2w.unique <- mydata2w %>%
distinct(mydata2w$ID, .keep_all=TRUE)
# Looking at the distribution of "Gender" in our sample size
table(mydata2w.unique$Gender)
##
## F M Non-Binary
## 60 21 5
# Mean Age of Sample Size
mydata2w.unique %>%
get_summary_stats(Age, type ="mean")
## # A tibble: 1 × 3
## variable n mean
## <fct><dbl><dbl>
## 1 Age 86 21.5
mydata2w.summarystat
## # A tibble: 6 × 15
## Beverage Sleep variable n min max median q1 q3 iqr mad mean
## <fct><fct><fct><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
## 1 Coffee 0 TestSco… 86 51.2 99.0 96.1 90.6 97.6 7.01 4.27 91.7
## 2 Coffee 5 TestSco… 86 64.3 99.0 97.6 94.8 99.0 4.22 2.09 95.7
## 3 Coffee 8 TestSco… 86 64.3 99.0 97.6 94.8 99.0 4.17 2.06 96.0
## 4 Placebo 0 TestSco… 86 -6.95 74.8 23.2 14.2 40.4 26.2 18.8 28.2
## 5 Placebo 5 TestSco… 86 -1.67 79.1 62.2 45.1 70.0 24.8 14.8 56.4
## 6 Placebo 8 TestSco… 86 30.0 79.1 71.6 64.9 74.9 10.1 6.82 67.5
## # ℹ 3 more variables: sd <dbl>, se <dbl>, ci <dbl>
Visualisation
# Line plots with multiple groups
anova2w.lineplot <-ggline(mydata2w, x = "Sleep", y = "TestScore", color = "Beverage",
add =c("mean_se"), #adding standard error bars
palette =c("tan4", "pink1"),
order =c("0", "5", "8"), #organizing order of levels
ylab = "Mean Test Score (%)", xlab = "Hours Slept (hrs)", #x- and y-axis title
linetype = "solid",
font.label =list(size = 20, color = "black"),
size = 1,
ggtheme =theme_gray(),
ylim=c(0,100) #adjusting the min and max value of y-aixs
)
# Change the appearance of titles and labels
anova2w.lineplot +
font("xlab", size = 14, color = "black", face = "bold")+
font("ylab", size = 14, color = "black", face = "bold")+
font("xy.text", size = 12, color = "black", face = "bold")
# Box plot with two factor variables
anova2w.boxplot <-ggboxplot(mydata2w, x = "Sleep", y = "TestScore",
color = "Beverage",
palette =c("tan", "black"),
ylab = "Mean Test Score (%)", xlab = "Hours Slept",
add = ("mean_ci"), #adding 95% confidence intervals
font.label =list(size = 20, color = "black"),
size = 1,
ggtheme =theme_gray()
)
# Change the appearance of titles and labels
anova2w.boxplot +
font("xlab", size = 14, color = "black", face = "bold")+
font("ylab", size = 14, color = "black", face = "bold")+
font("xy.text", size = 12, color = "black", face = "bold")
# Bar plot with two-factor variables
anova2w.barplot <-ggplot(mydata2w.summarystat,
aes(x =factor(Sleep), y = mean, fill = Beverage) ) +
geom_bar(stat = "identity", position = "dodge") +
geom_errorbar(aes(ymax = mean + sd, ymin = mean - sd), #adding standard error bars
position =position_dodge(0.9), width = 0.25, color = "Gray25") +
xlab("Hours Slept") +
ylab("Average Test Score (%)") +
scale_fill_brewer(palette = "RdPu") +
theme_classic2() +
theme(axis.line=element_line(linewidth=1.5), #thickness of x-axis line
axis.text =element_text(size = 14, colour = "black"),
axis.title =element_text(size = 16, colour = "black"),
panel.grid.major.y =element_line() ) +# adding horizontal grid lines
scale_y_continuous(breaks=seq(0, 100, 10)) # Ticks on y-axis from 0-100,
# jumps by 10
anova2w.barplot
Calcul de l’analyse de la variance bidirectionnelle à mesures répétées
mydata2w.2anova <-anova_test(
data = mydata2w,
dv = TestScore,
wid = ID,
within =c(Beverage, Sleep), #for more than 2 factors to count you must use c()
#it combines 2+ values into a vector/list
detailed = TRUE,
effect.size = "ges"#we get the generalized eta squared, enter pes to get
) # partial eta squared
# Apply the Greenhouse Geisser correction on an as-needed-basis
mydata2w.2anova <-get_anova_table(mydata2w.2anova, correction = "auto")
# We can calculate partial eta square and add it as a new column named "pes"
mydata2w.2anova$pes = mydata2w.2anova$SSn/(mydata2w.2anova$SSn + mydata2w.2anova$SSd)
# Calculate the mean sum of error and add it as a new column named "MSE"
mydata2w.2anova$MSE = mydata2w.2anova$SSd/mydata2w.2anova$DFd
# Convert tables to data frames
mydata2w.2anova <-data.frame(mydata2w.2anova)
# Deleting intercept info (first row)
mydata2w.2anova <- mydata2w.2anova[-1, ]
# Rename columns
colnames(mydata2w.2anova)[colnames(mydata2w.2anova) =="pes"] ="η2P"
colnames(mydata2w.2anova)[colnames(mydata2w.2anova) =="ges"] ="η2G"
colnames(mydata2w.2anova)[colnames(mydata2w.2anova) =="p..05"] ="sig"
mydata2w.2anova
## Effect DFn DFd SSn SSd F p sig η2G η2P MSE
## 2 Beverage 1.00 85.00 246966.79 18527.547 1133.025 6.47e-51 * 0.752 0.930 217.9711
## 3 Sleep 1.34 113.78 44095.24 12457.452 300.872 8.82e-39 * 0.351 0.779 109.4872
## 4 Beverage:Sleep 1.60 136.23 27663.08 9544.342 246.362 2.23e-41 * 0.254. 0,743-70,0605
Toute interaction bidirectionnelle significative dans cette analyse doit être suivie par d’autres analyses qui examinent l’effet d’un facteur alors que le niveau de l’autre facteur demeure constant.
Dans ces analyses, une interaction bidirectionnelle significative indique que l’impact d’un facteur (p. ex., la boisson) sur la variable de résultat (p. ex., le résultat du test) dépend du niveau de l’autre facteur (p. ex., le sommeil) (et vice-versa). Vous pouvez donc décomposer une interaction significative bidirectionnelle en traitant le facteur A comme une analyse de la variance à un facteur à chaque niveau du facteur B.
Procédure pour une interaction significative bidirectionnelle :
Effet principal simple : exécuter un modèle unidirectionnel d’un facteur alors que le niveau de l’autre facteur demeure constant. Il s’agit d’effectuer deux analyses de la variance unidirectionnelle pour un facteur à chaque niveau de l’autre facteur.
Comparaisons appariées simples : si le résultat de l’une ou l’autre des analyses unidirectionnelles est significatif, par exemple si l’effet principal simple est significatif, vous effectuerez des comparaisons appariées multiples pour déterminer quels groupes sont différents, comme nous l’avons fait précédemment pour le tutoriel sur l’analyse de la variance unidirectionnelle.
# Simple Main Effect
# Looking at the effect of Sleep separately for each level of Beverage
mydata2w.one.way <- mydata2w %>%
group_by(Beverage) %>%
anova_test(dv = TestScore, wid = ID, within = Sleep) %>%
get_anova_table() %>%
adjust_pvalue(method = "bonferroni")
mydata2w.one.way
## # A tibble: 2 × 9
## Beverage Effect DFn DFd F p `p<.05` ges p.adj
## <fct><chr><dbl><dbl><dbl><dbl><chr><dbl><dbl>
## 1 Coffee Sleep 1.17 99.4 22.4 2.11e- 6 * 0.07 4.22e- 6
## 2 Placebo Sleep 1.51 128. 331 1.76e-45 * 0.51 3.52e-45
L’effet du sommeil est significatif pour les deux niveaux du facteur « Boisson ». Nous devons maintenant procéder à des comparaisons appariées pour déterminer où se situe la différence entre les niveaux de sommeil et les deux niveaux de boisson.
# Pairwise comparisons between Sleep levels
mydata2w.pwc <- mydata2w %>%
group_by(Sleep) %>%
pairwise_t_test(
TestScore ~ Beverage, paired = TRUE,
p.adjust.method = "bonferroni"
)
mydata2w.pwc
## # Une trame de données « tibble » : 3 × 11
## Sleep .y. group1 group2 n1 n2 statistic df p p.adj p.adj.signif
## * <fct><chr><chr><chr> <int><int><dbl><dbl><dbl> <dbl><chr>
## 1 0 TestScore Coffee Placebo 86 86 35.1 85 2.32e-52 2.32e-52
## 2 5 TestScore Coffee Placebo 86 86 22.0 85 6.52e-37 6.52e-37
## 3 8 TestScore Coffee Placebo 86 86 25.7 85 8.84e-42 8.84e-42
Interprétation et analyse des résultats
L’analyse de la variance bidirectionnelle à mesures répétées a révélé un effet principal significatif de la boisson [F(1,85) = 1133,0, EQM = 218,0, eta-carré partiel = 0,930] et du sommeil [F(1,3-113,8) = 300,9, EQM = 109,5, eta-carré partiel = 0,780], nuancé par une interaction bidirectionnelle [F(1,6-136,2) = 300,9, EQM = 70,1, eta-carré partiel = 0,743]. Compte tenu de la valeur p ajustée de Bonferroni (p.adj), il semble que l’effet principal simple du sommeil est significatif aux deux niveaux de boisson [Café, p < 0,001; Placebo, p < 0,001]. Les comparaisons appariées montrent que le résultat moyen au test est significativement différent entre le café et le placebo à 0 heure (p < 0,0001), 5 heures (p < 0,0001) et 8 heures (p < 0,0001) de sommeil nocturne.
Notez qu’au lieu de décomposer l’interaction bidirectionnelle Sommeil x Boisson en un simple effet principal de la boisson, vous auriez pu choisir le sommeil. Comme dans le cas de l’analyse pour la variable « Sommeil » à chaque niveau de boisson, il n’est pas forcément nécessaire de réaliser celle-ci. Il suffit d’échanger les facteurs « Sommeil » et « Boisson » avec les codes pour les analyses post hoc.
Que se passerait-il dans les cas suivants? Interaction bidirectionnelle non significative
Il est important de noter que si l’interaction bidirectionnelle n’est pas significative dans l’analyse de la variance globale, mais qu’il existe toujours des effets principaux significatifs (p. ex., l’effet principal du sommeil et l’effet principal de la boisson), vous devez alors interpréter les effets principaux pour chacune des deux variables en effectuant des comparaisons appariées avec le test T.
# Comparisons for Beverage variable
mydata2w %>% pairwise_t_test( TestScore ~ Beverage, paired = TRUE, p.adjust.method = "bonferroni" )
# Comparisons for Sleep variable
mydata2w %>% pairwise_t_test( TestScore ~ Sleep, paired = TRUE, p.adjust.method = "bonferroni" )
Analyse de la variance mixte bidirectionnelle
Pour vous entraîner, je vous invite à calculer une analyse de la variance mixte bidirectionnelle pour le fichier de données « Practice_2WayANOVA_Mixed.xlsx » avec les éléments suivants :
- Variable mesurée : Résultat du test
- Conception mixte 2 x 3
- Interparticipant : « Exercice »
- Intraparticipant : « Sommeil »
Dans cette expérience hypothétique, les participants ont assisté à trois conférences à différents moments. Un groupe de participants a été invité à suivre des cours de yoga trois fois par semaine pendant trois mois avant la série de conférences et pendant l’expérience. Il leur a été demandé d’assister à une conférence, puis de revenir le lendemain pour répondre à un questionnaire à ce sujet après avoir dormi 0 heure, 5 heures ou 8 heures. Un autre groupe de participants a fait l’objet d’un protocole expérimental presque identique sauf qu’au lieu de pratiquer le yoga, il leur a été demandé de faire des exercices de pleine conscience.
Nous nous intéressons aux effets du sommeil sur l’apprentissage. Plus particulièrement, nous cherchons à savoir si les résultats des tests diminuent lorsque les participants ne dorment pas suffisamment. Nous souhaitons aussi vérifier si les personnes qui pratiquent le yoga sont plus résistantes à cette baisse de performance induite par le sommeil. Dans la mesure du possible, visualisez les données, puis calculez les tableaux récapitulatifs et les chiffres appropriés pour les comparer avant d’examiner les réponses ci-dessous.
# Data Prep & Summary Statistics
mydata2m <- read_excel("Practice_2WayANOVA_Mixed.xlsx")
mydata2m <- mydata2m %>% convert_as_factor(ID, Gender, Exercise, Sleep)
str(mydata2m)
## tibble [516 × 7] (S3 : tbl_df/tbl/data.frame)
## $ ...1 : chr [1:516] "259" "260" "261" "262" ...
## $ ID : Factor w/ 172 levels "23936","25440",..: 10 71 6 70 147 129 74 62 21 38 ...
## $ Gender : Factor w/ 3 levels "F","M","Non-Binary": 1 1 1 1 1 1 1 3 1 2 ...
## $ Age : num [1:516] 22 22 23 22 22 24 22 22 23 22 ...
## $ Exercise : Factor w/ 2 levels "Mindfulness",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Sleep : Factor w/ 3 levels "0","5","8": 1 1 1 1 1 1 1 1 1 1 ...
## $ TestScore: num [1:516] 32 43 44 46 31 45 21 48 40 32 ...
mydata2m.summarystat <- mydata2m %>%
group_by(Exercise, Sleep) %>%
get_summary_stats(TestScore, type = "full")
# Selecting rows of data only if ID value is unique
mydata2m.unique <- mydata2m %>%
distinct(mydata2m$ID, .keep_all=TRUE)
# Looking at the distribution of "Gender" per group
table(mydata2m.unique$Gender, mydata2m.unique$Exercise)
## Mindfulness Yoga
## F 40 60
## M 39 21
## Non-Binary 7 5
# Mean Age Per Group
mydata2m %>% group_by(Exercise) %>%
get_summary_stats(Age, type ="mean")
## # Une trame de données « tibble » : 2 × 4
## Exercise variable n mean
## <fct> <fct> <dbl> <dbl>
## 1 Mindfulness Age 258 22.6
## 2 Yoga Age 258 22.2
mydata2m.summarystat
## # Une trame de données « tibble » : 6 × 15
## Exercise Sleep variable n min max median q1 q3 iqr mad mean sd se ci
## <fct> <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Mindful… 0 TestSco… 86 15 48 32 23.2 41.8 18.5 13.3 32.3
## 2 Mindful… 5 TestSco… 86 38 80.8 48 43 53 10 7.41 48.7
## 3 Mindful… 8 TestSco… 86 55 66 59 57 63 6 4.45 60.1
## 4 Yoga 0 TestSco… 86 35 55 44.5 40.2 51 10.8 8.15 45.5
## 5 Yoga 5 TestSco… 86 66 80 72.5 68 76 8 6.67 72.7
## 6 Yoga 8 TestSco… 86 74 100 85.5 79 93.8 14.8 11.1 86.6
Je passerai ici l’étape de visualisation puisque vous pouvez utiliser les mêmes codes que ceux de l’analyse de la variance bidirectionnelle intrasujet. Je vous encourage à vous entraîner à créer des graphiques récapitulatifs.
Calcul de l’analyse de la variance bidirectionnelle
mydata2m.2anova <- anova_test(
data = mydata2m,
dv = TestScore,
wid = ID,
between = Exercise, #between-participants factor
within = Sleep,
detailed = TRUE,
effect.size = "ges"
)
# Apply the Greenhouse Geisser correction on an as-needed-basis
mydata2m.2anova <- get_anova_table(mydata2m.2anova, correction = "auto")
# Calculate partial eta square and add it as a new column named "pes"
mydata2m.2anova$pes = mydata2m.2anova$SSn/(mydata2m.2anova$SSn + mydata2m.2anova$SSd)
# Calculate the mean sum of error and add it as a new column named "MSE"
mydata2m.2anova$MSE = mydata2m.2anova$SSd/mydata2m.2anova$DFd
# Convert tables to data frames
mydata2m.2anova <- data.frame(mydata2m.2anova)
# Deleting intercept info (first row)
mydata2m.2anova <- mydata2m.2anova[-1, ]
# Rename columns
colnames(mydata2m.2anova)[colnames(mydata2m.2anova) == "pes"] ="η2P"
colnames(mydata2m.2anova)[colnames(mydata2m.2anova) == "ges"] ="η2G"
colnames(mydata2m.2anova)[colnames(mydata2m.2anova) == "p..05"] ="sig"
mydata2m.2anova
## Effect DFn DFd SSn SSd F p sig η2G η2P MSE
## 2 Exercise 1.00 170.0 57969.259 7475.35 1318.303 5.27e-82 * 0.699 0.8857759 43.97265
## 3 Sleep 1.89 321.9 104622.988 17468.35 1018.179 9.08e-137 * 0.807 0.8569239 54.26638
## 4 Exercise:Sleep 1.89 321.9 4365.807 17468.35 42.488 2.05e-16 * 0.149 0.1999531 54.26638
Procédure pour une interaction significative bidirectionnelle :
# Simple main effect of Exercise at each level of the Sleep factor
anova2m.one.way <- mydata2m %>%
group_by(Sleep) %>%
anova_test(dv = TestScore, wid = ID, between = Exercise) %>%
get_anova_table() %>%
adjust_pvalue(method = "bonferroni")
anova2m.one.way
## # Une trame de données « tibble » : 3 × 9
## Sleep Effect DFn DFd F p `p<.05` ges p.adj
## <fct> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <dbl> <dbl>
## 1 0 Exercise 1 170 109 4.39e-20 * 0.392 1.32e-19
## 2 5 Exercise 1 170 642 1.34e-59 * 0.791 4.02e-59
## 3 8 Exercise 1 170 742 6.34e-64 * 0.814 1.90e-63
OU vous pouvez choisir de décomposer l’interaction Exercice x Sommeil en un simple effet principal du sommeil pour chaque groupe d’exercice (code inclus ci-dessous); cependant, étant donné que nous savons que les résultats des tests diminuent lorsque les participants ne dorment pas suffisamment, ce n’est peut-être pas judicieux. Ici, nous allons plutôt comparer les résultats moyens des tests des deux groupes pour déterminer si le yoga atténue les baisses de performance liées à la durée du sommeil. Vous devez donc définir votre interaction bidirectionnelle afin qu’elle réponde à votre question de recherche.
# Simple main effect of Sleep for each Exercise Group
# anova2m.one.way.alt <- mydata2m %>%
# group_by(Exercise) %>%
# anova_test(dv = TestScore, wid = ID, within = Sleep) %>%
# get_anova_table() %>%
# adjust_pvalue(method = "bonferroni")
#anova2m.one.way.alt
L’effet principal simple de l’exercice étant significatif, nous devons maintenant effectuer des comparaisons appariées multiples pour déterminer quels groupes sont différents.
# Pairwise comparisons between Exercise levels
anova2m.pwc <- mydata2m %>%
group_by(Sleep) %>%
pairwise_t_test(TestScore ~ Exercise, p.adjust.method = "bonferroni")
anova2m.pwc
## # A tibble: 3 × 10
## Sleep .y. group1 group2 n1 n2 p p.signif p.adj p.adj.signif
## * <fct><chr><chr><chr><int><int><dbl><chr><dbl><chr>
## 1 0 TestS… Mindf… Yoga 86 86 4.39e-20 **** 4.39e-20 ****
## 2 5 TestS… Mindf… Yoga 86 86 1.34e-59 **** 1.34e-59 ****
## 3 8 TestS… Mindf… Yoga 86 86 6.34e-64 **** 6.34e-64 ****
# If you computed the simple main effect of sleep the pairwise comparisons would be as such:
#anova2m.pwc.alt <- mydata2m %>%
# group_by(Exercise) %>%
# pairwise_t_test(TestScore ~ Sleep, p.adjust.method = "bonferroni")
#anova2m.pwc.alt
Interprétation et rapports
EXEMPLE : L’analyse de la variance mixte bidirectionnelle a révélé un effet principal significatif de l’exercice [F(1,170) = 1318,3, EQM = 44,0, eta-carré partiel = 0,886] et du sommeil [F(1,9-322) = 1018,2, EQM = 54,3, eta-carré partiel = 0,857], nuancé par une interaction bidirectionnelle [F(1,9-322) = 42,5, EQM = 54,3, eta-carré partiel = 0,120]. Compte tenu de la valeur p ajustée de Bonferroni (p.adj), il semble que l’effet principal simple de l’exercice est significatif à tous les niveaux du facteur « Sommeil » [p < 0,001].
Les comparaisons appariées montrent que le résultat moyen au test était significativement différent entre les deux groupes « Exercice » pour tous les niveaux de sommeil, de sorte que le groupe « Yoga » a obtenu un résultat plus élevé au test que le groupe « Pleine conscience » lorsque les participants ont dormi 0 heure (13,1 %, p < 0,0001), 5 heures (24 %, p < 0,0001) ou 8 heures (26,5 %, p < 0,0001) la nuit précédant le test (voir figure 1).
Notez qu’ici, je renvoie mon lectorat à une figure (graphique à barres) afin de constater que le groupe « Yoga » a obtenu de meilleurs résultats que le groupe « Pleine conscience ». J’ai généré un graphique à barres pour ma figure 1 ici. Je vous encourage vivement à recréer ce graphique (avec les barres d’erreur type) ou tout autre type de graphique qui résume bien les données. De plus, à l’aide du tableau récapitulatif des statistiques, j’ai calculé la différence entre les résultats moyens des tests pour les comparaisons appropriées.