Two-Way ANOVA
The blueprint for Factorial Discovery. Two-Way ANOVA analyzes the synergistic interaction between two independent categorical factors on a single continuous outcome.
What is it?
Two-Way ANOVA examines how two independent categorical factors interactively and independently affect a single continuous dependent variable.
When to use it
- 2 Factors: Categorical independent variables (e.g. Treatment and Age).
- 1 Outcome: Continuous scale variable.
- Factorial Cells: Data collected for every combination of factors.
Core Idea
It isolates three effects: Main Effect of A, Main Effect of B, and the Interaction A×B (which shows if the effect of A depends on B):
If the lines are parallel, the effect of Factor A is identical across levels of Factor B. If lines cross or converge, a significant interaction is present.
Hypotheses
How it works
- Partition SS_Total into SS_A, SS_B, SS_AB, and SS_Error.
- Compute mean squares by dividing each by its df.
- Test three F-statistics against MS_Error.
Assumptions
Important Note
Quick Example
| Age / drug | Active | Placebo |
|---|---|---|
| Young | 82.3 | 54.1 |
| Old | 61.0 | 52.8 |
Two-Way ANOVA Live Laboratory
Manipulate main effects and interaction synergism to audit how variance divides into F-statistics.
| Source | SS | df | MS | F | p-value |
|---|---|---|---|---|---|
| Factor A | 0.0 | 1 | 0.0 | 0.00 | 1.0000 |
| Factor B | 0.0 | 1 | 0.0 | 0.00 | 1.0000 |
| Interaction (AB) | 0.0 | 1 | 0.0 | 0.00 | 1.0000 |
| Error (Within) | 2304.0 | 36 | 64.0 | - | - |
Hypotheses
Pragmatic null and alternative hypotheses defined in mathematical notation.
H₀ (Main effect A): All means across levels of Factor A are equal, averaging over Factor B. H₀ (Main effect B): All means across levels of Factor B are equal, averaging over Factor A. H₀ (Interaction): No interaction between Factor A and Factor B (effects of A are constant across levels of B, and vice versa).
Hₐ: At least one mean differs for Factor A OR Factor B, OR there is an A×B interaction (the effect of one factor depends on the level of the other factor).
Two-way ANOVA simultaneously tests THREE null hypotheses: main effect A, main effect B, and A×B interaction. CRITICAL: If interaction is significant, interpret main effects cautiously—they may be misleading. Instead, conduct simple effects analysis (effect of A at each level of B, or vice versa). With unbalanced designs, use Type III SS to properly partition variance.
Assumptions
The core mathematical criteria needed to ensure that statistical testing remains unbiased and valid.
Diagnostics
Checking residual plots and indices to examine model deviations and ensure standard error integrity.
- Levene's test for homogeneity of variance across all cells
- Q-Q plot of residuals to assess normality
- Boxplots by each cell to identify outliers
- Cell sample sizes (n per Factor A × Factor B combination)
- Interaction plot (lines for Factor A across Factor B, or vice versa) to visualize interaction
- Residual vs fitted values plot to check homoscedasticity and linearity
- Descriptive statistics (M, SD, n) per cell
- Variance comparison across cells (ratio of largest to smallest)
- Shapiro-Wilk test on residuals (if total n < 50)
- Cook's distance to identify influential cases
- Power analysis to check adequate power for detecting interaction
- Simple effects analysis if interaction is significant
- Profile plots showing means for each cell with error bars
Applied Minds
Review concrete study examples, data layout guidelines, and copy executable syntax scripts.
Gender × Treatment on Anxiety (2×2 Factorial Design)
Research question: Does the effectiveness of CBT for social anxiety differ by gender? Design: 2×2 factorial RCT with Gender (Male, Female) × Treatment (CBT, Waitlist Control), n=30 per cell (total N=120). Outcome: Social Phobia Inventory (SPIN) reduction score from baseline to 12 weeks (continuous, 0-68, higher = more reduction). Hypothesis: Interaction effect—CBT may be more effective for females than males.
# Two-way ANOVA: Gender × Treatment on Anxiety Reduction
# 2×2 factorial design with interaction
library(tidyverse)
library(car) # For Levene's test, Anova (Type III SS)
library(effectsize) # For effect sizes
library(emmeans) # For estimated marginal means and simple effects
library(ggpubr) # For publication-ready plots
set.seed(2025)
# Simulate realistic data with interaction
# CBT more effective for females than males
data <- data.frame(
gender = rep(c("Male", "Female"), each=60),
treatment = rep(rep(c("CBT", "Control"), each=30), 2),
spin_reduction = c(
# Male-CBT: moderate effect
rnorm(30, mean=18.5, sd=8.2),
# Male-Control: minimal change
rnorm(30, mean=3.8, sd=7.1),
# Female-CBT: large effect (INTERACTION)
rnorm(30, mean=26.3, sd=9.1),
# Female-Control: minimal change
rnorm(30, mean=4.2, sd=7.5)
)
) %>%
mutate(
gender = factor(gender, levels=c("Male", "Female")),
treatment = factor(treatment, levels=c("Control", "CBT"))
)
# === STEP 1: Descriptive Statistics by Cell ===
cat("=== Cell Descriptives(Mean ± SD) ===\n")
data %>%
group_by(gender, treatment) %>%
summarise(
n = n(),
M = mean(spin_reduction),
SD = sd(spin_reduction),
SE = SD/sqrt(n),
.groups = 'drop'
) %>%
print()
# === STEP 2: Check Assumptions ===
# 1. Check cell sizes (balance)
cat("\n=== Cell Sizes ===\n")
table(data$gender, data$treatment)
# 2. Levene's test (homogeneity across all 4 cells)
levene_result <- leveneTest(spin_reduction ~ gender * treatment, data=data)
cat("\n=== Levene's Test ===")
print(levene_result)
if (levene_result$`Pr(>F)`[1] > 0.05) {
cat("✓ Homogeneity of variance assumption met(p > .05)\n")
} else {
cat("⚠ Heteroscedasticity detected(p < .05). Consider transformation or robust methods.\n")
}
# 3. Normality of residuals
model_for_residuals <- lm(spin_reduction ~ gender * treatment, data=data)
par(mfrow=c(1,2))
plot(model_for_residuals, which=2, main="Q-Q Plot of Residuals") # Q-Q plot
hist(residuals(model_for_residuals), breaks=20, main="Histogram of Residuals",
xlab="Residuals", col="lightblue")
shapiro.test(residuals(model_for_residuals))
# 4. Outliers (boxplot by cell)
ggboxplot(data, x="treatment", y="spin_reduction", color="gender",
palette="jco", add="jitter",
title="SPIN Reduction by Gender and Treatment",
xlab="Treatment", ylab="SPIN Reduction Score")
# === STEP 3: Run Two-way ANOVA ===
# Fit model
anova_model <- lm(spin_reduction ~ gender * treatment, data=data)
# Type III SS (recommended for unbalanced designs; handles main effects with interaction)
anova_results <- Anova(anova_model, type=3)
cat("\n=== Two-way ANOVA Results(Type III SS) ===\n")
print(anova_results)
# Alternative: Type II SS (if no interaction expected)
# anova(anova_model) # Base R gives Type I SS (sequential, order-dependent - NOT recommended)
# === STEP 4: Effect Sizes ===
cat("\n=== Effect Sizes(Partial η²) ===\n")
eta_sq <- eta_squared(anova_results, partial=TRUE)
print(eta_sq)
# Omega squared (less biased)
omega_sq <- omega_squared(anova_results)
cat("\n=== Omega Squared(ω²) ===\n")
print(omega_sq)
# === STEP 5: Interaction Plot ===
interaction_data <- data %>%
group_by(gender, treatment) %>%
summarise(M = mean(spin_reduction),
SE = sd(spin_reduction)/sqrt(n()),
.groups='drop')
ggplot(interaction_data, aes(x=treatment, y=M, color=gender, group=gender)) +
geom_line(size=1.2) +
geom_point(size=4) +
geom_errorbar(aes(ymin=M-1.96*SE, ymax=M+1.96*SE), width=0.1) +
labs(title="Interaction: Gender × Treatment on Anxiety Reduction",
subtitle="Non-parallel lines indicate interaction",
x="Treatment Condition", y="Mean SPIN Reduction ± 95% CI",
color="Gender") +
scale_color_manual(values=c("Male"="#00BFC4", "Female"="#F8766D")) +
theme_classic(base_size=14) +
theme(legend.position="right")
# === STEP 6: Post-hoc Analysis ===
# If interaction is significant, conduct simple effects analysis
if (anova_results$`Pr(>F)`[4] < 0.05) {
cat("\n⚠ Interaction is SIGNIFICANT. Main effects may be misleading.\n")
cat("Conducting SIMPLE EFFECTS analysis...\n\n")
# Simple effects: Effect of Treatment at each level of Gender
emm <- emmeans(anova_model, ~ treatment | gender)
cat("=== Simple Effects: Treatment within each Gender ===\n")
pairs_simple <- pairs(emm, adjust="bonferroni")
print(pairs_simple)
# Effect sizes for simple effects
cat("\n=== Effect Sizes for Simple Effects(Cohen's d) ===\n")
# Male: CBT vs Control
male_cbt <- data %>% filter(gender=="Male", treatment=="CBT") %>% pull(spin_reduction)
male_ctrl <- data %>% filter(gender=="Male", treatment=="Control") %>% pull(spin_reduction)
d_male <- effsize::cohen.d(male_cbt, male_ctrl)$estimate
cat(sprintf("Male CBT vs Control: d = %.2f\n", d_male))
# Female: CBT vs Control
female_cbt <- data %>% filter(gender=="Female", treatment=="CBT") %>% pull(spin_reduction)
female_ctrl <- data %>% filter(gender=="Female", treatment=="Control") %>% pull(spin_reduction)
d_female <- effsize::cohen.d(female_cbt, female_ctrl)$estimate
cat(sprintf("Female CBT vs Control: d = %.2f\n", d_female))
} else {
cat("\n✓ No significant interaction. Main effects can be interpreted directly.\n")
# Post-hoc for main effects (if significant)
emm_gender <- emmeans(anova_model, ~ gender)
emm_treatment <- emmeans(anova_model, ~ treatment)
cat("\n=== Main Effect Pairwise Comparisons ===\n")
print(pairs(emm_gender, adjust="bonferroni"))
print(pairs(emm_treatment, adjust="bonferroni"))
}
# === STEP 7: Visualize Results ===
# Bar plot with facets
ggplot(interaction_data, aes(x=treatment, y=M, fill=gender)) +
geom_bar(stat="identity", position=position_dodge(0.9), width=0.8) +
geom_errorbar(aes(ymin=M-SE, ymax=M+SE),
position=position_dodge(0.9), width=0.2) +
labs(title="Social Anxiety Reduction: Gender × Treatment Interaction",
x="Treatment", y="Mean SPIN Reduction Score ± SE",
fill="Gender") +
scale_fill_brewer(palette="Set1") +
theme_classic(base_size=14)
# === APA-Style Report ===
cat("
=== APA-STYLE RESULTS ===
A 2×2 between-subjects ANOVA examined the effects of gender(Male, Female) and
treatment(CBT, Waitlist Control) on social anxiety reduction(SPIN scores).
Data met assumptions of homogeneity of variance(Levene's test, p = .18) and
normality of residuals(Shapiro-Wilk, p = .42). Cell sizes were balanced
(n = 30 per cell).
Results revealed a significant main effect of treatment, F(1, 116) = 78.45,
p < .001, partial η² = .40 (large effect), with CBT(M = 22.4, SD = 9.1)
producing greater anxiety reduction than control(M = 4.0, SD = 7.3). The main
effect of gender was not significant, F(1, 116) = 2.13, p = .15, partial η² = .02.
CRITICALLY, there was a significant Gender × Treatment interaction,
F(1, 116) = 4.89, p = .029, partial η² = .04 (small-medium effect). Simple
effects analysis revealed that CBT was effective for both genders, but the
effect was larger for females(M = 26.3 vs 4.2, d = 2.61, p < .001) than males
(M = 18.5 vs 3.8, d = 1.93, p < .001).
Conclusion: CBT significantly reduces social anxiety, with greater
effectiveness for females than males, supporting gender-tailored interventions.
")Main effect of treatment: F(1, 116) = 78.45, p < .001, partial η² = .40 (large). Gender × Treatment interaction: F(1, 116) = 4.89, p = .029, partial η² = .04 (small-medium). CRITICAL: Interaction significant—main effects alone are misleading. Simple effects show CBT effective for both genders, but more so for females (d = 2.61) than males (d = 1.93). Findings suggest gender moderates CBT effectiveness for social anxiety, warranting tailored interventions.
Alternatives
Structured fallback pathways for choosing alternative tests when normality or slopes requirements fail.
- Type III Sum of Squares — Mandate for unbalanced designs where cell N is unequal.
- Bootstrap Interaction — Verify the stability of the synergy term across sampling fluctuations.
- Weighted Least Squares — Downweight noisy demographic cells to protect the interaction signal.
- Robust Factorial GLM — Use M-estimators to neutralize the influence of cell-specific outliers.
- Permutation Factorial — Generate exact significance based on exhaustive group re-assignment.
Post-hoc
Group mean comparisons and correction controls (e.g. Tukey HSD, Bonferroni) to protect against Family-Wise Error Rates.
Post-hoc pairwise tests defined for this model.
If interaction is significant, main effects are often misleading. Focus on simple effects and interaction interpretation.
Effect Size
Understanding effect sizes (e.g., Cohen's d, Partial Eta-Squared) and clinical impact benchmarks.
Proportion of variance in DV explained by each factor/interaction, removing variance from other factors. Small: .01, Medium: .06, Large: .14 (Cohen, 1988). MOST COMMONLY REPORTED for factorial ANOVA.
Less biased estimate of population effect size. Small: .01, Medium: .06, Large: .14. Preferred over η² for small samples.
Total proportion of variance explained (sum across all factors). Biased upward; use partial η² instead in factorial designs.
Standardized effect size for ANOVA. Small: 0.10, Medium: 0.25, Large: 0.40. Used in power analysis.
0.2
0.5
0.8
Sample Size
Guidelines for minimum sample requirements and power analysis parameters.
The 'Cell Stability Mandate': A minimum of 15-20 participants per individual cell (e.g., Male-Treated) is essential. Factorial designs collapse mathematically if cell density is sparse.
| Effect Size | Parameters | Required n |
|---|---|---|
| Small Effect | f=.10 (Small) | n ≈ 787 total |
| Medium Effect | f=.25 (Medium) | n ≈ 128 total |
| Large Effect | f=.40 (Large) | n ≈ 52 total |
In a factorial world, empty or sparse cells are the 'Silent Killers' of discovery. Prioritize group balance to protect the integrity of the interaction F-test. If cells are unbalanced, the Type III Sum of Squares is the only valid path.
Reporting
How to compile statistical results into publication prose matching APA and journal style guides.
A two-way between-subjects ANOVA was conducted to examine the effects of Factor A: levels and Factor B: levels on DV. State design: 'The design was a [a × b factorial with balanced/unbalanced cell sizes (ns = list).'] Assumptions: 'Data met assumptions of homogeneity of variance (Levene's test, F(df1, df2) = X.XX, p = .XX) and normality of residuals (Shapiro-Wilk, p = .XX).' If violated, state corrections used. If unbalanced: 'Type III sums of squares were used due to unequal cell sizes.' Results revealed significant/non-significant main effects of Factor A, F(df1, df2) = X.XX, p = .XXX, partial η² = .XX interpret size, and Factor B, F(df1, df2) = X.XX, p = .XXX, partial η² = .XX. CRITICAL: State interaction result The Factor A × Factor B interaction was significant/non-significant, F(df1, df2) = X.XX, p = .XXX, partial η² = .XX. If interaction significant: 'The significant interaction indicates that the effect of [A depends on the level of B. Simple effects analysis was conducted...'] Describe simple effects results, pairwise comparisons, and interpretation in context. Conclude with practical significance and implications.
- F-statistics with df for BOTH main effects AND interaction
- p-values for all three F-tests
- Effect sizes (partial η²) for all effects
- Cell means, SDs, and ns (often in table)
- Statement about assumption checks (Levene's, normality)
- Type of SS if unbalanced (Type II or III)
- Simple effects results if interaction significant
- Post-hoc pairwise comparisons with corrections
- Interaction plot or cell means table
Manuscript Lab
Copy standard summary tables and forensic reporting grids to outline analysis details.
| Source | Type III SS | df | MS | F | p | ηp² |
|---|---|---|---|---|---|---|
| Treatment (Main) | 15.4 | 1 | 15.4 | 8.24 | .004 | .03 |
| Severity (Main) | 112.8 | 2 | 56.4 | 30.16 | < .001 | .20 |
| Treatment × Severity | 42.1 | 2 | 21.05 | 11.26 | < .001 | .09 |
| Error | 438.1 | 234 | 1.87 | — | — | — |
Identifies the Main Effects (individual factors) vs the Interaction (how factors work together).
The 'Pure' Variation. Calculates the unique variance slice owned by each factor after neutralizing all other overlaps.
Statistical Currency. Degrees of freedom spent to estimate each interaction and main effect.
Purified Variance. SS divided by df—the standardized metric for the Signal-to-Noise calculation.
The Multiplier. Measures how many times the effect outweighs random sampling error.
The Accident Probability. Probability of observing these differences if the true effect was zero. Target < .05.
The Relative Weight. The percentage of variance uniquely explained by this specific factor after removing error.
Command Center
Syntax libraries and function parameters for executing calculations in stats packages.
# 1. Fit Interaction Model
model <- lm(yield ~ treatment * severity, data = df)
# 2. Execute Type III Omnibus Audit
car::Anova(model, type = 3)
# 3. Visualize Synergy (Interaction Plot)
emmip(model, treatment ~ severity)Isolate synergistic effects instantly while auditing for homoscedasticity across the factorial grid.
# Multi-point Diagnostic Dashboard
performance::check_model(model)
# Map Estimated Marginal Means (EMMs)
sjPlot::plot_model(model, type = 'int')Common Mistakes
Analytical caveats and corrections to maintain modeling integrity.
References
Scholarly lineage and citation keys grounding the statistical framework.