Worksheet — One-Way ANOVA II: Running, Reporting & Extension

Reading the F table, finding which species differ, and writing it up

anova
statistics
project-setup

Hands-on companion to One-Way ANOVA II. Students run the ANOVA with aov() and car::Anova(), use emmeans to find which penguin species differ, and write a complete results sentence.

Author

Bill Perry

Published

September 10, 2026

One-Way ANOVA II — Which Species Differ, and How Do We Report It?

Recap from the ANOVA I worksheet

  • Fit mass_model <- lm(body_mass_g ~ species, data = penguins_df)
  • Checked normality of the residuals (plot(model), Shapiro-Wilk)
  • Checked equal variance (leveneTest())
  • Decision: assumptions look fine — proceed with classic ANOVA

Today’s Objectives

  1. Read the F table from aov() and car::Anova()
  2. Use emmeans to find which species differ
  3. Read a compact letter display
  4. Plot the estimated means ± CI
  5. Write a complete results sentence

How to use this worksheet

  • Work through the parts in order. Type the code into a new R script in Positron and run it line by line.
  • Blocks marked ▶ Run this should be executed as written.
  • Blocks marked ✏️ Your turn ask you to write, modify, or interpret.
  • The Extension at the end is done out of class (~30–40 min) and turned in with the worksheet.
Note🔮 Predict before you run — and type, don’t paste

Before each ▶ Run this block, cover the output and predict what R will print — the F, the p-value, which species differ. Then type the code yourself.


🧩 Chunk 1 — Run the ANOVA (after lecture Chunk 1)

Parts 7–8: read the F table two ways.

Setup · Reload libraries and refit the model

▶ Run this at the top of your script:

# Load packages at the top — always ----------------------
library(tidyverse)      # wrangling + ggplot2
library(palmerpenguins) # the penguins data
library(car)            # Anova()
library(emmeans)        # post-hoc pairwise tests

▶ Run this (same model as the end of the ANOVA I worksheet):

penguins_df <- penguins %>%
  drop_na(body_mass_g, species)

mass_model <- lm(body_mass_g ~ species, data = penguins_df)

Part 7 · Run the ANOVA

🔮 Predict first: The boxplot looked very separated. Predict the p-value — near 0.5, near 0.05, or far below 0.001?

▶ Run this:

# Classic ANOVA table ------------------------------------
mass_aov <- aov(body_mass_g ~ species, data = penguins_df)
summary(mass_aov)

✏️ Your turn: Read the F table.

Df (between, within) =        ,
F value =
p-value =
Reject H₀ at α = 0.05?  Y / N

Part 8 · The same table with car::Anova()

▶ Run this:

# Type II sums of squares (the habit to build) ----------
Anova(mass_model, type = "II")

⚠️ Watch out! Lowercase anova() (base R) and capital Anova() (car) are different functions. If R says could not find function "Anova", you forgot library(car).

✏️ Your turn: Does Anova() give the same F and p as summary(mass_aov)? (For one predictor it should.)

Match?  Y / N
Why do we prefer Anova(type = "II") as a habit? (hint: unbalanced / two-way later)

🧩 Chunk 2 — Which groups differ? (after lecture Chunk 2)

Parts 9–11: post-hoc comparisons, letters, plot, and the write-up.

Part 9 · Post-hoc — which species differ?

🔮 Predict first: Will all three species differ from each other, or will two be statistically tied? Commit before you run it.

▶ Run this:

# Estimated marginal means + Tukey-adjusted pairs -------
mass_emm <- emmeans(mass_model, pairwise ~ species)

mass_emm$contrasts

✏️ Your turn: Record each pairwise comparison.

Adelie  vs Chinstrap:  p =        different?  Y / N
Adelie  vs Gentoo:     p =        different?  Y / N
Chinstrap vs Gentoo:   p =        different?  Y / N

▶ Run this (compact letter display):

# Groups sharing a letter are NOT different -------------
multcomp::cld(mass_emm$emmeans, Letters = letters)

✏️ Your turn: Write the letter for each species. Do any share a letter?

Adelie:      Chinstrap:      Gentoo:
Any species share a letter (i.e. not different)?  Y / N

Part 10 · Plot the result

▶ Run this:

# Estimated means with 95% CI ---------------------------
mass_emm_plot <- as.data.frame(mass_emm$emmeans) %>%
  ggplot(aes(x = species, y = emmean, color = species)) +
  geom_point(size = 3) +
  geom_errorbar(aes(ymin = lower.CL, ymax = upper.CL),
                width = 0.15, linewidth = 0.9) +
  labs(x = "Species", y = "Estimated mean mass (g)") +
  theme_minimal() +
  theme(legend.position = "none")

mass_emm_plot
# Save it to the figures folder -------------------------
ggsave("figures/penguin_mass_emmeans.png",
       plot = mass_emm_plot,
       width = 5, height = 5, units = "in", dpi = 300)

✏️ Your turn: Do any of the confidence intervals overlap? What does that suggest?

Your answer:

Part 11 · Write a results sentence

✏️ Your turn: Write a complete results sentence. Include the test, F, both df, the p-value, the post-hoc outcome, and the direction (which is heaviest).

Template:
"Body mass differed significantly among the three penguin species
(one-way ANOVA: F(__, __) = ____, p ____). Tukey-adjusted comparisons
showed that ____________________, with __________ heaviest."

Your sentence:

Part 12 · Review and checkpoint

At this point you should be able to:

✏️ Your turn — before you move on: Run your whole script top to bottom with Ctrl/Cmd + Shift + Enter. Does it run cleanly?

Ran cleanly?  Y / N
If not, what error appeared:

Extension — out of class (~30–40 min)

Turn this in with your worksheet. It runs the exact analysis you just did — but on one half of the data that only you chose, so your F, p, and letters will be your own.

Important

The prediction and explanation in E2 and E3 must be handwritten on paper, photographed, and embedded in your submission (![caption](my_photo.jpg)). Typed answers to E2/E3 receive at most half credit, even if correct — I want to see your own reasoning develop on the page.

E1 · Refit on one sex only (3 pts)

Everyone above ran the identical analysis on the full dataset, so everyone’s numbers match. Not anymore. Pick one sex — "male" or "female" — and re-run the whole ANOVA on only that half. Write down which you picked before you start.

# Replace ___ with "male" or "female"
penguins_sex <- penguins %>%
  drop_na(body_mass_g, species, sex) %>%
  filter(sex == "___")

sex_model <- lm(body_mass_g ~ species, data = penguins_sex)

Anova(sex_model, type = "II")                     # new F, df, p
emmeans(sex_model, pairwise ~ species)$contrasts  # which pairs differ now

✏️ Record:

Sex chosen:
n (nrow(penguins_sex)):
F(__, __) =            p =
Pairs that differ now (Adelie–Chinstrap / Adelie–Gentoo / Chinstrap–Gentoo):

E2 · Predict, then check — ✍️ by hand (3 pts)

Before running E1, on paper, write:

  1. Will restricting to one sex change which species pairs differ, compared with your full-data result in Part 9?
  2. Will the F-statistic get bigger or smaller, and why? Think about what happens to (a) sample size and (b) within-group spread when you drop half the penguins.

Then run E1 and, still by hand, write your actual F and p and whether your prediction held.

E3 · Explain it — ✍️ by hand, with YOUR numbers (3 pts)

Using your real output from E1 (not the class example):

  1. State H₀ and Hₐ for your one-sex test.
  2. In plain language, say which species differ and which don’t — and why a significant overall F by itself would not have told you that.
  3. Compare your E1 F and p to the full-data F and p from Part 7. Explain why they differ (or are surprisingly similar) even though you cut n roughly in half.

Optional, no points: rerun E1 for flipper_length_mm instead of body_mass_g and see whether the species separate the same way.


Getting unstuck

  1. could not find function "Anova"library(car), and mind the capital A (base anova() is different).
  2. could not find function "emmeans"library(emmeans).
  3. cld not foundinstall.packages("multcomp"), then multcomp::cld(...).
  4. Cheat sheetshttps://posit.co/resources/cheatsheets/

💡 Key idea: Nothing about aov(), Anova(), or emmeans() here was specific to penguins or to having exactly three species — swap in four treatments or five sites and the same three function calls, in the same order, give you the same kind of answer.


End of the ANOVA II worksheet. Next: kicking off the final project.