The three assumptions ANOVA makes that panel data violates
Repeated-measures ANOVA is still the default analysis in many cosmetics labs. It produces a familiar output, it is easy to run in Excel or SPSS, and reviewers know how to read it. The problem is that it rests on three assumptions that cosmetic panel data systematically violates:
- Sphericity: the variance of differences between all pairs of time points must be equal. In practice, differences between adjacent time points are usually smaller than differences between distant time points. Mauchly's test of sphericity is almost always significant for studies with more than 3 time points.
- Balanced data: RM-ANOVA assumes every subject has a measurement at every time point. Missing data requires listwise deletion: you lose the entire subject if one time point is missing. Mixed models use all available data.
- Independence of residuals: observations from the same subject are correlated by definition. ANOVA handles this through the within-subject partition, but only when the design is perfectly balanced.
What a random effect actually does
The key insight in mixed models is that subjects are a random sample from a population, not a fixed set of interest. You do not care about the specific effect of Subject 14: you care about the variance in response across all subjects.
The random intercept (1 | subject_id) allows each subject to have their own baseline level. This absorbs between-subject variability and lets the fixed effects (treatment, time) be estimated more precisely. It also handles missing data: a subject missing at time point 3 still contributes their time points 1, 2, and 4 to the model.
LMM for a typical cosmetic study
library(lme4)
library(emmeans)
# Panel study: hydration measured at BL, D14, D28, D56
# Some panelists missing at D28 (life happens)
model_lmm <- lmer(
hydration ~ visit + (1 | subject_id),
data = panel_data,
REML = TRUE
)
summary(model_lmm)
# Post-hoc: change from baseline at each visit
emm <- emmeans(model_lmm, ~ visit)
contrast(emm, method = "trt.vs.ctrl", ref = "BL") %>%
summary(infer = TRUE, adjust = "none")
The emmeans output gives estimated marginal means at each visit with standard errors and confidence intervals, plus the pairwise contrasts vs baseline. This is what you report in the efficacy summary.
Interpreting emmeans output
A typical output looks like:
contrast estimate SE df t.ratio p.value
D14 - BL 3.42 0.84 89 4.07 0.0001
D28 - BL 6.71 0.91 89 7.37 <0.0001
D56 - BL 8.93 0.95 89 9.40 <0.0001
The estimate is the mean change from baseline. The SE and confidence intervals give you the uncertainty. The p-value tests whether the change is different from zero. For a claim like "hydration improved significantly after 4 weeks," you look at the D28 row.
For the claim dossier, you also want the confint() output, the 95% CI for the change. This is the "honest" representation of your claim's magnitude under Criterion 4 of EU 655/2013.
Handling ordinal endpoints with CLMM
Not all cosmetic endpoints are continuous. Expert-graded scales (roughness, radiance, firmness on a 0-4 scale) are ordinal: the distance between 2 and 3 is not necessarily the same as between 1 and 2. Treating ordinal data as continuous inflates Type I error.
library(ordinal)
# Skin roughness graded 0-4 by a trained evaluator
# Cumulative link mixed model (CLMM) = ordinal LMM
model_clmm <- clmm(
roughness_ord ~ visit + (1 | subject_id),
data = panel_data,
link = "logit"
)
# Odds ratios for change at each visit
exp(coef(model_clmm))
The CLMM estimates odds ratios for being in a higher category at each visit compared to baseline. An OR of 0.35 for D28 means panelists were 65% less likely to be in a higher (worse) roughness category after 4 weeks, a natural way to communicate the improvement.
Model diagnostics you should always run
library(performance)
# Comprehensive LMM diagnostics
check_model(model_lmm)
# Key checks:
# 1. Residuals vs fitted (no systematic pattern)
# 2. QQ plot of random effects (approximately normal)
# 3. Scale-location (constant variance)
# 4. Influence points (no single subject driving the result)
For ordinal models, also check the proportional odds assumption:
# Test proportional odds: is the cumulative logit model appropriate?
nominal_test(model_clmm)
When ANOVA is still acceptable
Use RM-ANOVA when: the study is perfectly balanced with no missing data, you have only two time points (baseline and one follow-up), and you have already confirmed sphericity is not an issue (rare for more than two time points). In practice, this is almost never the case for cosmetic panel studies.
When in doubt, use a mixed model. It reduces to RM-ANOVA for balanced complete data, so you lose nothing by using it and gain robustness to the situations that inevitably arise in real studies.
Key takeaway
The practical difference between RM-ANOVA and LMM is small in code (five minutes to switch) and potentially large in results (missing data, unequal variances, ordinal outcomes). There is no good reason to use ANOVA for cosmetic panel data in 2025.