Two-way ANOVA
Compares the mean of a measurement between the groups formed by two factors, and tests whether the effect of one factor depends on the other (an interaction).
Statistically reviewed by Daniel Wolff on 5 November 2026.
Clinical example
Mean hemoglobin six weeks after starting one of two iron supplements, in men and in women. Two factors (supplement and sex), one measurement per patient, and the question is whether the supplement works differently in men and women. In a real trial you would also adjust for each patient's baseline hemoglobin, with an ANCOVA that includes the interaction.
When to use it
- The outcome is a measurement on a continuous scale, one per patient.
- The groups are formed by combining two categorical factors, such as supplement and sex, and each patient is in one combination.
- You want the effect of each factor, or you want to know whether the effect of one factor depends on the other (an interaction). A typical clinical use: one factor is randomized (the supplement) and the other is a patient characteristic (sex). The interaction is then a subgroup question, and it should be prespecified.
- For one factor use one-way ANOVA. To adjust for a baseline value or for continuous variables, see ANCOVA or linear regression.
Assumptions to check
Independent observations
One value per patient, and no clustering. Repeated measurements or clustered patients need a mixed model.
Roughly normal residuals, no extreme outliers
Look at a histogram or Q-Q plot of the residuals. With moderate, similar-sized groups, mild non-normality is usually acceptable. A single extreme value can still drive the result. For a skewed, always-positive outcome, the same model can be fitted on the log scale, and the effects are then ratios. There is no simple rank-based two-way equivalent of the Kruskal-Wallis test. Whether an interaction exists depends on the scale: effects that are equal as ratios are unequal as differences, and the other way round. An interaction on the raw scale can vanish on the log scale.
Equal spread in every combination
Compare the SDs of the combinations. A much larger SD in one of them is a warning sign. It matters most when the numbers per combination differ. Consider a transformation or robust (sandwich) standard errors, for example HC3 (car::Anova with white.adjust = "hc3" in R). Don't let Levene's test, which SPSS prints with HOMOGENEITY, decide on its own.
Enough patients in every combination
Empty or very small combinations make the estimates, and especially the interaction, unstable.
Unequal numbers per combination
With unequal numbers, the test of each factor depends on how the sums of squares are calculated. R's anova() uses sequential (Type I) tests, so the main-effect rows depend on the order of the terms, while SPSS uses Type III by default. The interaction row is the same either way. In R, Type III main effects only make sense with sum-to-zero contrasts: car::Anova(fit, type = 3) with R's default treatment coding gives wrong main-effect tests. Without an important interaction, many statisticians prefer Type II (car::Anova(fit, type = 2)). Estimates and CIs from the fitted model (emmeans in R) avoid the problem.
What to report
- Mean with SD, and the number of patients, for each combination of the two factors
- The test of the interaction: the F statistic with both degrees of freedom, and the exact p-value
- The interaction itself as an estimate: the difference between the effects of one factor in the two levels of the other (for example the supplement effect in women minus that in men), with its 95% CI
- If there is no important interaction: the effect of each factor as a difference in means with 95% CI. If there is: the effect of one factor within each level of the other, with 95% CI
- How pairwise comparisons were adjusted for multiple comparisons
- How the factors were coded, and the type of sums of squares if the numbers per combination differ
- The number of patients analyzed, and how many were left out because of missing data
Common mistakes
Don't run many separate tests
Testing every pair of groups, outcome or time point separately piles up false positives. Use one model, prespecify the main comparison, and correct for multiple comparisons where needed.
Don't make a second factor by dichotomizing
Cutting a continuous variable such as age into groups to get a second factor throws away information and power, and the cut-off is often arbitrary. Keep it continuous: see linear regression.
Main effects are averages when there is an interaction
With an interaction, the main effect of a factor is an average over the levels of the other factor. It does not describe any single group, and it can mislead.
A subgroup that is significant is not an interaction
Concluding that an effect differs between subgroups because it is significant in one and not in the other is wrong. Test the interaction, or compare the effects directly.
A non-significant interaction is not 'no interaction'
Interaction tests usually have low power: detecting an interaction as large as the main effect needs roughly four times the sample size (Brookes et al. 2004).
Don't read summary(fit) as the main effects
With an interaction in the model and R's default treatment coding, the coefficient of supplement is the effect in the reference sex only, not the overall effect. Use emmeans, and anova(fit) for the interaction test.
R and SPSS can disagree with unequal groups
They use different types of sums of squares by default, so the main-effect tests can differ even for the same data.
Numeric codes in R
Coding a factor as numbers (1, 2, 3) without making it a factor fits one slope instead of comparing the groups, and gives no error.
Alternatives
| If your situation is… | Use |
|---|---|
| Only one factor | One-way ANOVA |
| A baseline measurement is available, as in a trial | ANCOVA (adjusting for baseline) |
| You need to adjust for continuous variables, or the spreads are clearly unequal (regression with robust standard errors) | Linear regression |
| Strongly skewed outcome or outliers: fit the same model on the log scale, so the effects become ratios | Unpaired t-test on log-transformed data |
| The outcome is a count | Poisson and negative binomial regression |
| The same patients are measured under every combination, or patients are clustered | Linear mixed model |
| Yes/no outcome | Logistic regression |
In your software
# R
d$supplement <- factor(d$supplement, levels = c("standard", "new")) # the order sets the direction below
d$sex <- factor(d$sex, levels = c("female", "male"))
fit <- lm(hb ~ supplement * sex, data = d) # both factors and their interaction
anova(fit) # the supplement:sex row is the test of the interaction
nobs(fit) # patients used: missing values are dropped silently
library(emmeans)
# effect of supplement within each sex, with 95% CI (new minus standard)
confint(pairs(emmeans(fit, ~ supplement | sex), reverse = TRUE))
# no important interaction: the supplement effect averaged over sex
emmeans(fit, ~ supplement)
# the interaction itself: (new minus standard) in women minus that in men
confint(contrast(emmeans(fit, ~ supplement * sex), interaction = c("revpairwise", "pairwise")))
interaction.plot(d$sex, d$supplement, d$hb) # roughly parallel lines: little interaction
# Don't read summary(fit): the supplement coefficient is the effect in the first sex only
* Type III sums of squares are the SPSS default. EMMEANS compares supplement within each sex.
* Read Tests of Between-Subjects Effects for the interaction row, and Pairwise Comparisons for the effect within each sex.
* The profile plot shows estimated means, not the raw data.
UNIANOVA hb BY supplement sex
/METHOD=SSTYPE(3)
/PLOT=PROFILE(sex*supplement)
/EMMEANS=TABLES(supplement*sex) COMPARE(supplement) ADJ(BONFERRONI)
/PRINT=DESCRIPTIVE HOMOGENEITY
/CRITERIA=ALPHA(.05)
/DESIGN=supplement sex supplement*sex.