Power for designs with two or more measurement points
Mixed-effects models
Power
R
Author
Nora Wickelmaier
Published
October 9, 2026
Question from statistics consulting
A few weeks ago, we met to talk about a power analysis for my pre/post study. Now, I want to add a second delayed post-test assessment one week later. Does this change my power simulation?
Short answer: Yes, quite dramatically. ;)
Power simulation for ANCOVA (two time points)
This power simulation for the original pre/post design assumed that the data will be analyzed with an ANCOVA model (based on the paper by Vickers and Altman 2001). This is the original power simulation based on values found for previous data collected by this PhD student.
n <-87# Draw plausible pre values and setup data framedat <-data.frame(pre =rnorm(n, mean =12.5, sd =3.5),grp =factor(rep(1:3, each = n/3), labels =c("g1", "g2", "g3")))# Specify the minimum relevant average treatment effects and set the remaining# parameters to plausible values# mu = mean learning outcome (expected pre-post effect in reference group)# b = slope between pre and post values# a2 = expected difference between group 2 and group 1# a3 = expected difference between group 3 and group 1# se = sigma of ANCOVA modelbeta <-c(mu =3, b = .7, a2 =1.5, a3 =3)means <-model.matrix(~ pre + grp, dat) %*% betase <-2# Estimate powerpval <-replicate(2000, { post <- means +rnorm(n, sd = se) m <-lm(post ~ pre + grp, dat)summary(m)$coef["grpg2", "Pr(>|t|)"]})mean(pval <0.05)
[1] 0.8015
I will not go into detail, why this is the appropriate analysis for this design. Maybe this will become another post at some point. Read the paper by Vickers and Altman (2001) in the meantime.
Power simulation for mixed-effects model (three time points)
Unfortunately, when we add another time point into the mix, we now have to switch the analysis to a mixed-effects model with a group x time interaction. It was also much harder for the PhD student to term her hypothesis in terms of model parameters. So, I made her draw the expected pattern and add mean values to the plot. This is what she came up with.
n <-3design <-data.frame(id =factor(rep(1:n, each =3)),time =factor(rep(c("pre", "post", "delayed"), times = n),levels =c("pre", "post", "delayed")),grp =factor(rep(1:3, each =3, times = n /3),labels =c("g1", "g2", "g3")))means <-c(5.5, 6.8, 6.5, 5.5, 6.8, 6.5, 5.5, 8.5, 8)lattice::xyplot(means ~ time, data = design, groups = grp,type ="a",ylim =0:10,auto.key =TRUE)
We can now use these means and the “design” data frame to calculate the fixed model parameters with
\[
\mathbf{\beta} = X^{-1} \mathbf{\mu}
\]
mm <-model.matrix(~ grp * time, data = design)(beta <-as.vector(solve(mm) %*% means))
[1] 5.5 0.0 0.0 1.3 1.0 0.0 1.7 0.0 1.5
Now, we still need the variance terms for the random effects. For simplicity reasons, we still stick to a random intercept model here. So we need to specify a residual variance and a person variance.
sp <-1.5se <-2
The power simulation then looks something like this.
library("lme4")n <-120dat <-data.frame(id =factor(rep(1:n, each =3)),time =factor(rep(c("pre", "post", "delayed"), times = n),levels =c("pre", "post", "delayed")),grp =factor(rep(1:3, each =3, times = n /3),labels =c("g1", "g2", "g3")))mm <-model.matrix(~ grp * time, data = dat)means <-as.vector(mm %*% beta)pval <-replicate(1000, { y <- means +# structural partrep(rnorm(n, mean =0, sd = sp), each =3) +# person effectrnorm(n *3, mean =0, sd = se) # random error m0 <-lmer(y ~ grp + time + (1| id), data = dat, REML =FALSE) m1 <-lmer(y ~ grp * time + (1| id), data = dat, REML =FALSE)# Likelihood-Ratio-Testanova(m0, m1)["m1", "Pr(>Chisq)"] })mean(pval <0.05)
[1] 0.774
For more examples how to run power simulations in R see Wickelmaier (2026).
Session info
sessionInfo()
R version 4.6.1 (2026-06-24 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)
Matrix products: default
LAPACK version 3.12.1
locale:
[1] LC_COLLATE=German_Germany.utf8 LC_CTYPE=German_Germany.utf8
[3] LC_MONETARY=German_Germany.utf8 LC_NUMERIC=C
[5] LC_TIME=German_Germany.utf8
time zone: Europe/Berlin
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] lme4_2.1-0 Matrix_1.7-6
loaded via a namespace (and not attached):
[1] digest_0.6.39 fastmap_1.2.0 xfun_0.62 lattice_0.23-1
[5] splines_4.6.1 knitr_1.52 htmltools_0.5.9 rmarkdown_2.32
[9] Rdpack_2.6.6 cli_3.6.6 nloptr_2.2.1 grid_4.6.1
[13] reformulas_0.4.4 compiler_4.6.1 rbibutils_2.4.1 boot_1.3-32
[17] tools_4.6.1 nlme_3.1-171 minqa_1.2.8 evaluate_1.0.5
[21] Rcpp_1.1.2 yaml_2.3.12 otel_0.2.0 rlang_1.3.0
[25] jsonlite_2.0.0 htmlwidgets_1.6.4 MASS_7.3-66
References
Vickers, A. J., and D. G. Altman. 2001. “Analysing Controlled Trials with Baseline and Follow up Measurements.”British Medical Journal 323 (7321): 1123–24. https://doi.org/10.1136/bmj.323.7321.1123.