Multiple Factor Parametric Tests - R#

Factorial ANOVA#

  • Samples: ≥2

  • Levels: ≥2

  • Between or Within Subjects: Between

  • Reporting: “Figure 8 shows an interaction plot with ±1 standard deviation error bars for X1 and X2. A factorial ANOVA indicated a statistically significant effect on Y of X1 (F(1, 56) = 9.35, p < .01) and X2 (F(1, 56) = 4.30, p < .05), but not of the X1×X2 interaction (F(1, 56) = 0.42, n.s.).”

# Example data
# df has subjects (S), two between-Ss factors (X1,X2) each w/levels (a,b), and continuous response (Y)
df <- read.csv("data/2F2LBs.csv")
head(df, 20)
A data.frame: 20 × 4
SX1X2Y
<int><chr><chr><dbl>
1 1aa 9.939918
2 2ab12.271865
3 3ba11.062279
4 4bb12.763591
5 5aa 7.980861
6 6ab10.468626
7 7ba21.071315
8 8bb15.270048
9 9aa22.125037
1010ab10.955709
1111ba14.496724
1212bb12.731080
1313aa14.110583
1414ab13.587539
1515ba13.175990
1616bb14.313530
1717aa11.595727
1818ab13.723466
1919ba17.276898
2020bb14.945042
library(ez) # for ezANOVA

df$S = factor(df$S) # Subject id is nominal
df$X1 = factor(df$X1) # X1 is a 2-level factor
df$X2 = factor(df$X2) # X2 is a 2-level factor

m = ezANOVA(dv=Y, between=c(X1,X2), wid=S, type=3, data=df) # use c() for >1 factors
m$Levene # if p<.05, we have a violation of homoscedasticity, so use a White-adjusted ANOVA
Coefficient covariances computed by hccm()
A data.frame: 1 × 7
DFnDFdSSnSSdFpp<.05
<int><int><dbl><dbl><dbl><dbl><chr>
35636.93165149.76874.6030360.005989026*
m= ezANOVA(dv=Y, between=c(X1,X2), wid=S, type=3, data=df, white.adjust=TRUE) # heteroscedastic
m$ANOVA
Coefficient covariances computed by hccm()
A data.frame: 3 × 6
EffectDFnDFdFpp<.05
<chr><dbl><dbl><dbl><dbl><chr>
2X1 1568.73091930.004569383*
3X2 1564.00948970.050096183
4X1:X21560.38760810.536086422
# the following also performs the same factorial ANOVA
m = aov(Y ~ X1*X2, data=df)
anova(m)
A anova: 4 × 5
DfSum SqMean SqF valuePr(>F)
<int><dbl><dbl><dbl><dbl>
X1 1 60.03946860.0394689.35455640.003408605
X2 1 27.57185427.5718544.29588180.042819437
X1:X2 1 2.665445 2.6654450.41529440.521925508
Residuals56359.419527 6.418206 NA NA
library(ggplot2)
library(ggthemes)
library(scales)
library(plyr)

# Interaction plot
# http://www.sthda.com/english/wiki/ggplot2-line-plot-quick-start-guide-r-software-and-data-visualization
# http://www.sthda.com/english/wiki/ggplot2-error-bars-quick-start-guide-r-software-and-data-visualization
# http://www.sthda.com/english/wiki/ggplot2-point-shapes
# http://www.sthda.com/english/wiki/ggplot2-line-types-how-to-change-line-types-of-a-graph-in-r-software
df2 <- ddply(df, ~ X1*X2, function(d) # make a summary data table
  c(NROW(d$Y),
    sum(is.na(d$Y)),
    sum(!is.na(d$Y)),
    mean(d$Y, na.rm=TRUE),
    sd(d$Y, na.rm=TRUE),
    median(d$Y, na.rm=TRUE),
    IQR(d$Y, na.rm=TRUE)))
colnames(df2) <- c("X1","X2","Rows","NAs","NotNAs","Mean","SD","Median","IQR")
ggplot(data=df2, aes(x=X1, y=Mean, color=X2, group=X2)) + theme_minimal() + 
  # set the font styles for the plot title and axis titles
  theme(plot.title   = element_text(face="bold",  color="black", size=18, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.title.x = element_text(face="bold",  color="black", size=16, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.title.y = element_text(face="bold",  color="black", size=16, hjust=0.5, vjust=0.0, angle=90)) + 
  # set the font styles for the value labels that show on each axis
  theme(axis.text.x  = element_text(face="plain", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.text.y  = element_text(face="plain", color="black", size=12, hjust=0.0, vjust=0.5, angle=0)) + 
  # set the font styles for the legend
  theme(legend.title = element_text(face="bold", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) +
  theme(legend.text  = element_text(face="plain", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) +
  # create the plot lines, points, and error bars
  geom_line(aes(linetype=X2), position=position_dodge(0.05)) + 
  geom_point(aes(shape=X2, size=X2), position=position_dodge(0.05)) + 
  geom_errorbar(aes(ymin=Mean-SD, ymax=Mean+SD), position=position_dodge(0.05), width=0.1) + 
  # place text labels on each bar
  geom_text(aes(label=sprintf("%.2f (±%.2f)", Mean, SD)), position=position_dodge(0.05), hjust=0.0, vjust=-1.0, color="black", size=3.5) +
  # set the labels for the title and each axis
  labs(title="Y by X1, X2", x="X1", y="Y") + 
  # set the ranges and value labels for each axis
  scale_x_discrete(labels=c("a","b")) + 
  scale_y_continuous(breaks=seq(0,20,by=5), labels=seq(0,20,by=5), limits=c(0,20), oob=rescale_none) + 
  # set the name, labels, and colors for the traces
  scale_color_manual(name="X2", labels=c("a","b"), values=c("red", "blue")) +
  # set the size and shape of the points
  scale_size_manual(values=c(4,4)) +
  scale_shape_manual(values=c(16,10)) +
  # set the linetype of the lines
  scale_linetype_manual(values=c("solid", "longdash"))
../../_images/multiple-factor-tests-r_6_0.png

Linear Model (LM)#

  • Samples: ≥2

  • Levels: ≥2

  • Between or Within Subjects: Between

  • Reporting: “Figure 8 shows an interaction plot with ±1 standard deviation error bars for X1 and X2. An analysis of variance indicated a statistically significant effect on Y of X1 (F(1, 56) = 9.35, p < .01) and X2 (F(1, 56) = 4.30, p < .05), but not of the X1×X2 interaction (F(1, 56) = 0.42, n.s.).”

# Example data
# df has subjects (S), two between-Ss factors (X1,X2) each w/levels (a,b), and continuous response (Y)
df <- read.csv("data/2F2LBs.csv")
head(df, 20)
A data.frame: 20 × 4
SX1X2Y
<int><chr><chr><dbl>
1 1aa 9.939918
2 2ab12.271865
3 3ba11.062279
4 4bb12.763591
5 5aa 7.980861
6 6ab10.468626
7 7ba21.071315
8 8bb15.270048
9 9aa22.125037
1010ab10.955709
1111ba14.496724
1212bb12.731080
1313aa14.110583
1414ab13.587539
1515ba13.175990
1616bb14.313530
1717aa11.595727
1818ab13.723466
1919ba17.276898
2020bb14.945042
df$S = factor(df$S) # Subject id is nominal (unused)
df$X1 = factor(df$X1) # X1 is a 2-level factor
df$X2 = factor(df$X2) # X2 is a 2-level factor
m = lm(Y ~ X1*X2, data=df)
anova(m)
A anova: 4 × 5
DfSum SqMean SqF valuePr(>F)
<int><dbl><dbl><dbl><dbl>
X1 1 60.03946860.0394689.35455640.003408605
X2 1 27.57185427.5718544.29588180.042819437
X1:X2 1 2.665445 2.6654450.41529440.521925508
Residuals56359.419527 6.418206 NA NA
library(ggplot2)
library(ggthemes)
library(scales)
library(plyr)

# Interaction plot
# http://www.sthda.com/english/wiki/ggplot2-line-plot-quick-start-guide-r-software-and-data-visualization
# http://www.sthda.com/english/wiki/ggplot2-error-bars-quick-start-guide-r-software-and-data-visualization
# http://www.sthda.com/english/wiki/ggplot2-point-shapes
# http://www.sthda.com/english/wiki/ggplot2-line-types-how-to-change-line-types-of-a-graph-in-r-software
df2 <- ddply(df, ~ X1*X2, function(d) # make a summary data table
  c(NROW(d$Y),
    sum(is.na(d$Y)),
    sum(!is.na(d$Y)),
    mean(d$Y, na.rm=TRUE),
    sd(d$Y, na.rm=TRUE),
    median(d$Y, na.rm=TRUE),
    IQR(d$Y, na.rm=TRUE)))
colnames(df2) <- c("X1","X2","Rows","NAs","NotNAs","Mean","SD","Median","IQR")
ggplot(data=df2, aes(x=X1, y=Mean, color=X2, group=X2)) + theme_minimal() + 
  # set the font styles for the plot title and axis titles
  theme(plot.title   = element_text(face="bold",  color="black", size=18, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.title.x = element_text(face="bold",  color="black", size=16, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.title.y = element_text(face="bold",  color="black", size=16, hjust=0.5, vjust=0.0, angle=90)) + 
  # set the font styles for the value labels that show on each axis
  theme(axis.text.x  = element_text(face="plain", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.text.y  = element_text(face="plain", color="black", size=12, hjust=0.0, vjust=0.5, angle=0)) + 
  # set the font styles for the legend
  theme(legend.title = element_text(face="bold", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) +
  theme(legend.text  = element_text(face="plain", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) +
  # create the plot lines, points, and error bars
  geom_line(aes(linetype=X2), position=position_dodge(0.05)) + 
  geom_point(aes(shape=X2, size=X2), position=position_dodge(0.05)) + 
  geom_errorbar(aes(ymin=Mean-SD, ymax=Mean+SD), position=position_dodge(0.05), width=0.1) + 
  # place text labels on each bar
  geom_text(aes(label=sprintf("%.2f (±%.2f)", Mean, SD)), position=position_dodge(0.05), hjust=0.0, vjust=-1.0, color="black", size=3.5) +
  # set the labels for the title and each axis
  labs(title="Y by X1, X2", x="X1", y="Y") + 
  # set the ranges and value labels for each axis
  scale_x_discrete(labels=c("a","b")) + 
  scale_y_continuous(breaks=seq(0,20,by=5), labels=seq(0,20,by=5), limits=c(0,20), oob=rescale_none) + 
  # set the name, labels, and colors for the traces
  scale_color_manual(name="X2", labels=c("a","b"), values=c("red", "blue")) +
  # set the size and shape of the points
  scale_size_manual(values=c(4,4)) +
  scale_shape_manual(values=c(16,10)) +
  # set the linetype of the lines
  scale_linetype_manual(values=c("solid", "longdash"))
../../_images/multiple-factor-tests-r_10_0.png

Factorial Repeated Measures ANOVA#

  • Samples: ≥2

  • Levels: ≥2

  • Between or Within Subjects: Within

  • Reporting: “Figure 9 shows an interaction plot with ±1 standard deviation error bars for X1 and X2. A factorial repeated measures ANOVA indicated a statistically significant effect on Y of X1 (F(1, 14) = 5.45, p < .05), but not of X2 (F(1, 14) = 0.18, n.s.), or of the X1×X2 interaction (F(1, 14) = 0.12, n.s.).”

# Example data
# df has subjects (S), two within-Ss factors (X1,X2) each w/levels (a,b), and continuous response (Y)
df <- read.csv("data/2F2LWs.csv")
head(df, 20)
A data.frame: 20 × 4
SX1X2Y
<int><chr><chr><dbl>
11aa11.4749213
21ab14.8431021
31ba 0.1430649
41bb15.0210806
52aa16.9487115
62ab12.9088880
72ba 7.3433619
82bb11.1885220
93aa12.3397073
103ab14.8390688
113ba21.5131754
123bb 0.2390247
134aa11.7081257
144ab13.8740498
154ba 4.3615137
164bb 2.3686943
175aa15.3668561
185ab13.6340391
195ba 4.9401806
205bb13.5781464
library(ez) # for ezANOVA

df$S = factor(df$S) # Subject id is nominal
df$X1 = factor(df$X1) # X1 is a 2-level factor
df$X2 = factor(df$X2) # X2 is a 2-level factor

m = ezANOVA(dv=Y, within=c(X1,X2), wid=S, type=3, data=df) # use c() for >1 factors
m$Mauchly # p<.05 indicates a sphericity violation
NULL
m$ANOVA # use if no violation
A data.frame: 3 × 7
EffectDFnDFdFpp<.05ges
<chr><dbl><dbl><dbl><dbl><chr><dbl>
2X1 1145.45370880.03492658*0.070392015
3X2 1140.17979490.67799256 0.003762924
4X1:X21140.12231750.73173975 0.002019078
# if there is a sphericity violation, report the Greenhouse-Geisser or Huynh-Feldt correction
p = match(m$Sphericity$Effect, m$ANOVA$Effect) # positions of within-Ss effects in m$ANOVA
m$Sphericity$GGe.DFn = m$Sphericity$GGe * m$ANOVA$DFn[p] # Greenhouse-Geisser DFs
m$Sphericity$GGe.DFd = m$Sphericity$GGe * m$ANOVA$DFd[p]
m$Sphericity$HFe.DFn = m$Sphericity$HFe * m$ANOVA$DFn[p] # Huynh-Feldt DFs
m$Sphericity$HFe.DFd = m$Sphericity$HFe * m$ANOVA$DFd[p]
m$Sphericity # show results
$GGe.DFn
$GGe.DFd
$HFe.DFn
$HFe.DFd
# the following also performs the equivalent repeated measures ANOVA, but does not address sphericity
m = aov(Y ~ X1*X2 + Error(S/(X1*X2)), data=df)
summary(m)
Error: S
          Df Sum Sq Mean Sq F value Pr(>F)
Residuals 14  592.5   42.32               

Error: S:X1
          Df Sum Sq Mean Sq F value Pr(>F)  
X1         1  160.3  160.28   5.454 0.0349 *
Residuals 14  411.4   29.39                 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Error: S:X2
          Df Sum Sq Mean Sq F value Pr(>F)
X2         1    8.0    8.00    0.18  0.678
Residuals 14  622.5   44.47               

Error: S:X1:X2
          Df Sum Sq Mean Sq F value Pr(>F)
X1:X2      1    4.3    4.28   0.122  0.732
Residuals 14  490.1   35.01               
# Interaction plot
# http://www.sthda.com/english/wiki/ggplot2-line-plot-quick-start-guide-r-software-and-data-visualization
# http://www.sthda.com/english/wiki/ggplot2-error-bars-quick-start-guide-r-software-and-data-visualization
# http://www.sthda.com/english/wiki/ggplot2-point-shapes
# http://www.sthda.com/english/wiki/ggplot2-line-types-how-to-change-line-types-of-a-graph-in-r-software
df2 <- ddply(df, ~ X1*X2, function(d) # make a summary data table
  c(NROW(d$Y),
    sum(is.na(d$Y)),
    sum(!is.na(d$Y)),
    mean(d$Y, na.rm=TRUE),
    sd(d$Y, na.rm=TRUE),
    median(d$Y, na.rm=TRUE),
    IQR(d$Y, na.rm=TRUE)))
colnames(df2) <- c("X1","X2","Rows","NAs","NotNAs","Mean","SD","Median","IQR")
ggplot(data=df2, aes(x=X1, y=Mean, color=X2, group=X2)) + theme_minimal() + 
  # set the font styles for the plot title and axis titles
  theme(plot.title   = element_text(face="bold",  color="black", size=18, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.title.x = element_text(face="bold",  color="black", size=16, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.title.y = element_text(face="bold",  color="black", size=16, hjust=0.5, vjust=0.0, angle=90)) + 
  # set the font styles for the value labels that show on each axis
  theme(axis.text.x  = element_text(face="plain", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.text.y  = element_text(face="plain", color="black", size=12, hjust=0.0, vjust=0.5, angle=0)) + 
  # set the font styles for the legend
  theme(legend.title = element_text(face="bold", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) +
  theme(legend.text  = element_text(face="plain", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) +
  # create the plot lines, points, and error bars
  geom_line(aes(linetype=X2), position=position_dodge(0.05)) + 
  geom_point(aes(shape=X2, size=X2), position=position_dodge(0.05)) + 
  geom_errorbar(aes(ymin=Mean-SD, ymax=Mean+SD), position=position_dodge(0.05), width=0.1) + 
  # place text labels on each bar
  geom_text(aes(label=sprintf("%.2f (±%.2f)", Mean, SD)), position=position_dodge(0.05), hjust=0.0, vjust=-1.0, color="black", size=3.5) +
  # set the labels for the title and each axis
  labs(title="Y by X1, X2", x="X1", y="Y") + 
  # set the ranges and value labels for each axis
  scale_x_discrete(labels=c("a","b")) + 
  scale_y_continuous(breaks=seq(0,20,by=5), labels=seq(0,20,by=5), limits=c(0,20), oob=rescale_none) + 
  # set the name, labels, and colors for the traces
  scale_color_manual(name="X2", labels=c("a","b"), values=c("red", "blue")) +
  # set the size and shape of the points
  scale_size_manual(values=c(4,4)) +
  scale_shape_manual(values=c(16,10)) +
  # set the linetype of the lines
  scale_linetype_manual(values=c("solid", "longdash"))
../../_images/multiple-factor-tests-r_17_0.png

Linear Mixed Model (LMM)#

  • Samples: ≥2

  • Levels: ≥2

  • Between or Within Subjects: Within

  • Reporting: “Figure 9 shows an interaction plot with ±1 standard deviation error bars for X1 and X2. A linear mixed model analysis of variance indicated a statistically significant effect on Y of X1 (F(1, 42) = 4.42, p < .05), but not of X2 (F(1, 42) = 0.22, n.s.), or of the X1×X2 interaction (F(1, 42) = 0.12, n.s.).”

# Example data
# df has subjects (S), two within-Ss factors (X1,X2) each w/levels (a,b), and continuous response (Y)
df <- read.csv("data/2F2LWs.csv")
head(df, 20)
A data.frame: 20 × 4
SX1X2Y
<int><chr><chr><dbl>
11aa11.4749213
21ab14.8431021
31ba 0.1430649
41bb15.0210806
52aa16.9487115
62ab12.9088880
72ba 7.3433619
82bb11.1885220
93aa12.3397073
103ab14.8390688
113ba21.5131754
123bb 0.2390247
134aa11.7081257
144ab13.8740498
154ba 4.3615137
164bb 2.3686943
175aa15.3668561
185ab13.6340391
195ba 4.9401806
205bb13.5781464
library(lme4) # for lmer
library(car) # for Anova
df$S = factor(df$S) # Subject id is nominal
df$X1 = factor(df$X1) # X1 is a 2-level factor
df$X2 = factor(df$X2) # X2 is a 2-level factor
contrasts(df$X1) <- "contr.sum"
contrasts(df$X2) <- "contr.sum"
m = lmer(Y ~ X1*X2 + (1|S), data=df)
Anova(m, type=3, test.statistic="F")
Loading required package: Matrix
Loading required package: carData
A anova: 4 × 4
FDfDf.resPr(>F)
<dbl><dbl><dbl><dbl>
(Intercept)148.42975411147.698544e-09
X1 4.41676571424.162596e-02
X2 0.22031471426.412279e-01
X1:X2 0.11800811427.329188e-01
# Interaction plot
# http://www.sthda.com/english/wiki/ggplot2-line-plot-quick-start-guide-r-software-and-data-visualization
# http://www.sthda.com/english/wiki/ggplot2-error-bars-quick-start-guide-r-software-and-data-visualization
# http://www.sthda.com/english/wiki/ggplot2-point-shapes
# http://www.sthda.com/english/wiki/ggplot2-line-types-how-to-change-line-types-of-a-graph-in-r-software
df2 <- ddply(df, ~ X1*X2, function(d) # make a summary data table
  c(NROW(d$Y),
    sum(is.na(d$Y)),
    sum(!is.na(d$Y)),
    mean(d$Y, na.rm=TRUE),
    sd(d$Y, na.rm=TRUE),
    median(d$Y, na.rm=TRUE),
    IQR(d$Y, na.rm=TRUE)))
colnames(df2) <- c("X1","X2","Rows","NAs","NotNAs","Mean","SD","Median","IQR")
ggplot(data=df2, aes(x=X1, y=Mean, color=X2, group=X2)) + theme_minimal() + 
  # set the font styles for the plot title and axis titles
  theme(plot.title   = element_text(face="bold",  color="black", size=18, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.title.x = element_text(face="bold",  color="black", size=16, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.title.y = element_text(face="bold",  color="black", size=16, hjust=0.5, vjust=0.0, angle=90)) + 
  # set the font styles for the value labels that show on each axis
  theme(axis.text.x  = element_text(face="plain", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) + 
  theme(axis.text.y  = element_text(face="plain", color="black", size=12, hjust=0.0, vjust=0.5, angle=0)) + 
  # set the font styles for the legend
  theme(legend.title = element_text(face="bold", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) +
  theme(legend.text  = element_text(face="plain", color="black", size=14, hjust=0.5, vjust=0.0, angle=0)) +
  # create the plot lines, points, and error bars
  geom_line(aes(linetype=X2), position=position_dodge(0.05)) + 
  geom_point(aes(shape=X2, size=X2), position=position_dodge(0.05)) + 
  geom_errorbar(aes(ymin=Mean-SD, ymax=Mean+SD), position=position_dodge(0.05), width=0.1) + 
  # place text labels on each bar
  geom_text(aes(label=sprintf("%.2f (±%.2f)", Mean, SD)), position=position_dodge(0.05), hjust=0.0, vjust=-1.0, color="black", size=3.5) +
  # set the labels for the title and each axis
  labs(title="Y by X1, X2", x="X1", y="Y") + 
  # set the ranges and value labels for each axis
  scale_x_discrete(labels=c("a","b")) + 
  scale_y_continuous(breaks=seq(0,20,by=5), labels=seq(0,20,by=5), limits=c(0,20), oob=rescale_none) + 
  # set the name, labels, and colors for the traces
  scale_color_manual(name="X2", labels=c("a","b"), values=c("red", "blue")) +
  # set the size and shape of the points
  scale_size_manual(values=c(4,4)) +
  scale_shape_manual(values=c(16,10)) +
  # set the linetype of the lines
  scale_linetype_manual(values=c("solid", "longdash"))
../../_images/multiple-factor-tests-r_21_0.png