Setting: two within-subject repeated measures and one between-subject measure
Here’s Jeff Witmer’s code for this.
HowellData.orig <-
read.table("http://www.uvm.edu/~dhowell/methods8/DataFiles/Tab14-11.dat", header = TRUE)
dv <- numeric()
for (i in 1:24) {
w <- with( HowellData.orig, c(C1P1[i], C1P2[i], C2P1[i], C2P2[i], C3P1[i], C3P2[i], C4P1[i], C4P2[i]))
dv <- c(dv, w)
}
Phase <- factor(rep(1:2, times = 96))
Cycle <- factor(rep(1:4, each = 2, times = 24))
Group = factor(rep(1:3, each = 64))
Subj <- factor(rep(1:24, each = 8))
HowellDataFramed <- data.frame(dv, Phase, Cycle, Group, Subj)
HowellDataFramed %>% filter(Subj == "1")
## dv Phase Cycle Group Subj
## 1 1 1 1 1 1
## 2 28 2 1 1 1
## 3 22 1 2 1 1
## 4 48 2 2 1 1
## 5 22 1 3 1 1
## 6 50 2 3 1 1
## 7 14 1 4 1 1
## 8 48 2 4 1 1
This method (suggested by R Pruim) uses tidyr and dplyr to make the data transformations clearer.
HowellData.orig <-
read.table("http://www.uvm.edu/~dhowell/methods8/DataFiles/Tab14-11.dat", header = TRUE)
HowellData <-
HowellData.orig %>%
mutate(Subj = factor(1:nrow(HowellData.orig))) %>%
gather(key = "key", value = "dv", C1P1:C4P2) %>%
separate(key, into = c("Cycle", "Phase"), sep = 2) %>%
mutate(Group = factor(Group),
Cycle = factor(readr::parse_number(Cycle)), # extract number
Phase = factor(readr::parse_number(Phase))) # extract number
HowellData %>% filter(Subj == "1")
## Group Subj Cycle Phase dv
## 1 1 1 1 1 1
## 2 1 1 1 2 28
## 3 1 1 2 1 22
## 4 1 1 2 2 48
## 5 1 1 3 1 22
## 6 1 1 3 2 50
## 7 1 1 4 1 14
## 8 1 1 4 2 48
# pull the 7 F statistics out of a model object
extractStats <- function(model) {
smodel <- summary(model)
c(
group = smodel[[1]][[1]][[4]][[1]],
cycle = smodel[[2]][[1]][[4]][[1]],
grpxcycle = smodel[[2]][[1]][[4]][[2]],
phase = smodel[[3]][[1]][[4]][[1]],
grpxphase = smodel[[3]][[1]][[4]][[2]],
cycxphs = smodel[[4]][[1]][[4]][[1]],
grpxcycxphs = smodel[[4]][[1]][[4]][[2]]
)
}
# This can be omitted by setting contrasts in each call to aov()
# options.before <- options(contrasts = c("contr.sum","contr.poly"))
model1 <-
aov(dv ~ (Group*Cycle*Phase) + Error(Subj/(Cycle*Phase)), data = HowellData,
contrasts = contr.sum)
print(summary(model1)) # Standard repeated measures anova
##
## Error: Subj
## Df Sum Sq Mean Sq F value Pr(>F)
## Group 2 4617 2308.4 3.083 0.067 .
## Residuals 21 15723 748.7
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Error: Subj:Cycle
## Df Sum Sq Mean Sq F value Pr(>F)
## Cycle 3 2727 909.0 12.027 2.53e-06 ***
## Group:Cycle 6 1047 174.5 2.309 0.0445 *
## Residuals 63 4761 75.6
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Error: Subj:Phase
## Df Sum Sq Mean Sq F value Pr(>F)
## Phase 1 11703 11703 129.85 1.88e-10 ***
## Group:Phase 2 4054 2027 22.49 6.01e-06 ***
## Residuals 21 1893 90
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Error: Subj:Cycle:Phase
## Df Sum Sq Mean Sq F value Pr(>F)
## Cycle:Phase 3 742 247.17 4.035 0.01090 *
## Group:Cycle:Phase 6 1274 212.30 3.466 0.00505 **
## Residuals 63 3859 61.26
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
observed <- extractStats(model1)
We use a helper function that samples “block-wise”.
# x is a vector
# block is a vector of equal length
# x[i] and x[j] should match if block[i] == block[j]
# the result is a vector where x values have been permuted "block-wise"
blockSample <- function(x, blocks) {
id <- as.numeric(factor(blocks))
blocks <- aggregate(x, by = list(blocks), FUN = function(x) head(x, 1))$x
sample(blocks)[id]
}
(This is still brittle with respect to the variable names used, and one could imagine fancier versions that avoid this.)
randomData <- function(data) {
data %>%
mutate(
dv.orig = dv,
dv = mosaic::shuffle(dv, groups = Subj), # this is using mosaic::shuffle()
Group = blockSample(Group, blocks = Subj)
)
}
randomModel <- function() {
aov(dv ~ (Group*Cycle*Phase) + Error(Subj/(Cycle*Phase)),
data = randomData(HowellDataFramed),
contrasts = contr.sum)
}
SimStats <- do(1000) * extractStats(randomModel())
# Summarise permuted data
SimStats %>%
summarise(
probgroup = prop(group >= observed["group"]),
probcycle = prop(cycle >= observed["cycle"]),
probgrpxcycle = prop(grpxcycle >= observed["grpxcycle"]),
probphase = prop(phase >= observed["phase"]),
probgrpxphase = prop(grpxphase >= observed["grpxphase"]),
probcycxphs = prop(cycxphs >= observed["cycxphs"]),
probgrpxcycxphs = prop(grpxcycxphs >= observed["grpxcycxphs"])
)
## probgroup probcycle probgrpxcycle probphase probgrpxphase probcycxphs
## 1 0.064 0 0.044 0 0 0.008
## probgrpxcycxphs
## 1 0.005