One-Way Repeated-Measures ANOVA

repeated-measures
anova
sphericity
mauchly
greenhouse-geisser
Comparing means across three or more within-subjects measurements, with sphericity checks and corrections
Published

April 17, 2026

Research question

Use a one-way repeated-measures ANOVA when the same units are measured on the same outcome at three or more occasions and you want to test whether the mean changes across occasions. Biomedical example: does a cohort’s C-reactive protein concentration differ across baseline, day 7, day 14, and day 28 after a surgical procedure?

Assumptions

Assumption How to verify in R
Within-subjects measurements design
Outcome approximately normal at each occasion shapiro_test() by time
Sphericity: variances of differences between any two occasions are equal Mauchly’s test via rstatix::anova_test()
No extreme outliers boxplot per time

If sphericity fails, apply the Greenhouse-Geisser or Huynh-Feldt correction to the F test. When normality fails, use the Friedman test.

Hypotheses

\[H_0: \mu_1 = \mu_2 = \ldots = \mu_k \qquad H_1: \text{at least one time point differs}\]

R code

library(tidyverse); library(rstatix); library(afex); library(effectsize); library(ggstatsplot)
set.seed(42)

# 30 patients; CRP (mg/L) at 4 occasions
crp <- expand_grid(id = 1:30, time = factor(c("d0", "d7", "d14", "d28"),
                                            levels = c("d0", "d7", "d14", "d28"))) |>
  mutate(base = rep(rnorm(30, 6, 1.5), each = 4),
         crp  = case_when(
           time == "d0"  ~ base + rnorm(n(), 40, 8),
           time == "d7"  ~ base + rnorm(n(), 18, 6),
           time == "d14" ~ base + rnorm(n(), 8,  4),
           time == "d28" ~ base + rnorm(n(), 3,  2)
         ))

crp |> group_by(time) |> shapiro_test(crp)

aov_rm <- anova_test(data = crp, dv = crp, wid = id, within = time)
get_anova_table(aov_rm, correction = "auto")   # applies GG if sphericity fails

# Post-hoc pairwise comparisons with Bonferroni correction
crp |> pairwise_t_test(crp ~ time, paired = TRUE, p.adjust.method = "bonferroni")

# Effect size: generalized eta-squared (reported by rstatix)
aov_rm

ggwithinstats(data = crp, x = time, y = crp, type = "parametric",
              xlab = "Time", ylab = "CRP (mg/L)")

Interpreting the output

The output reports F, numerator and denominator df, the Mauchly test statistic, and a corrected p-value if sphericity is violated. With \(F(2.1, 60.8) = 210\) after Greenhouse-Geisser correction and \(p < .001\), the null of equal means is rejected. Generalised eta-squared \(\approx 0.73\) is very large.

Pairwise comparisons show that each adjacent time point differs from its predecessor (Bonferroni-adjusted \(p < .01\)), and every non-baseline time point differs from day 0.

Effect size

Generalised eta-squared (\(\eta_G^2\)) is preferred for repeated-measures designs because it is comparable across between- and within-subjects designs. Cohen’s thresholds (adapted): small 0.01, medium 0.06, large 0.14.

Reporting (APA 7)

A one-way repeated-measures ANOVA showed a significant main effect of time on CRP, F(2.1, 60.8) = 210, p < .001, eta_G^2 = .73. Mauchly’s test indicated a violation of sphericity (W = .58, p < .01), so the Greenhouse-Geisser correction was applied. Bonferroni-adjusted pairwise comparisons confirmed a step-wise decrease from day 0 through day 28 (all p < .01).

Common pitfalls

  • Ignoring sphericity and reporting the uncorrected F test when Mauchly rejects.
  • Treating repeated measures as independent and running a one-way ANOVA; this inflates Type I error.
  • Missing data: a subject missing any time point is dropped in classical rmANOVA. Use a linear mixed model (lme4::lmer) for unbalanced data.
  • Reporting only the omnibus result; report the pattern via pairwise contrasts or a plot.

Parametric vs. non-parametric alternative

Further reading


Structure inspired by the University of Zurich Methodenberatung (methodenberatung.uzh.ch). All text, examples, R code, and reporting sentences are independently authored in English.