One-Way ANOVA in R

Info sheet · Statistics for Psychology & Neuroscience

Author

Andrew Bell

Published

August 13, 2026

Info sheet 0.2 (draft) · Prerequisites: the one-way ANOVA & F-ratio · Give feedback ↗

Working notes for the author — not shown to students once collapsed; remove before publishing.

  • [ ]

What you’ll get from this sheet

The practical how‑to: running a one‑way ANOVA in R. By the end you should be able to:

  1. Run an ANOVA two ways — with aov() and lm() — and see they agree.
  2. Use the fitted object to check assumptions (Q–Q, Shapiro–Wilk, Levene’s).

aov(dv ~ iv, data) and lm(dv ~ iv, data) fit the same model — identical F and p. aov() shows the familiar ANOVA table; lm() shows coefficients (group differences from a reference). Force your grouping variable to a factor first.

Two functions, one answer

Doing ANOVA by hand is instructive but, as Danielle Navarro puts it, “doing ANOVA calculations yourself sucks.” R gives you two base‑R functions, and they work almost identically: aov() and lm(). The syntax is the same — object <- function(dv ~ iv, data = ...) — but the output differs: aov() gives you the ANOVA table you know (df, SS, MS, F, p), while lm() fits the linear model and reports coefficients. Same fit underneath, same F and p at the bottom. That’s the ANOVA‑is‑regression point made concrete.

One prep step: make sure your grouping variable is a factor (decade <- as.factor(decade)). If an ANOVA misbehaves in R, an unconverted factor is the usual culprit.

Same fit, two views

Here’s a one‑way ANOVA on three groups. Flip between how aov() and lm() report it — different tables, identical F.

The aov() view partitions variance into group and Residuals and hands you the F‑ratio directly. The lm() view fits score = intercept + groupB + groupC — the intercept is group A’s mean, and each coefficient is a group’s difference from A (dummy coding) — but prints the identical F and p at the bottom. Different questions, same model.

See it in code

score = [52 55 48 50 54 61 58 63 60 59 70 66 72 69 68];
group = [1 1 1 1 1 2 2 2 2 2 3 3 3 3 3];
[p, tbl, stats] = anova1(score, group);   % ANOVA table + F and p

The R and Python tabs run live; MATLAB is a static reference.

You run aov() and lm() on the same data. The aov() summary shows F = 65.7; the lm() summary shows a pile of coefficients. Did you fit two different things?

No — it’s one model shown two ways. aov() formats it as a variance‑partition table; lm() formats it as regression coefficients (intercept = reference‑group mean, other terms = differences from it). The F‑statistic, its degrees of freedom, and the p‑value are identical in both — which is exactly why we say ANOVA is a special case of the general linear model.

The commonest R hiccup here is forgetting to make the grouping variable a factor — if it’s stored as a number, lm/aov may treat it as a continuous predictor and fit one slope instead of group means. Convert with as.factor(). Also don’t confuse the two outputs: read aov()’s table for the omnibus F, and remember lm()’s individual coefficients are differences from the reference group, not group means.

Where this shows up next

Once your ANOVA is significant, follow up with post‑hoc tests or planned contrasts to find which groups differ — but first check the assumptions. See Chapter (ANOVA).