Worksheet — One-Way ANOVA II: Running, Reporting & Extension
Reading the F table, finding which species differ, and writing it up
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.
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
- Read the F table from
aov()andcar::Anova() - Use
emmeansto find which species differ - Read a compact letter display
- Plot the estimated means ± CI
- 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.
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 capitalAnova()(car) are different functions. If R sayscould not find function "Anova", you forgotlibrary(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.
The prediction and explanation in E2 and E3 must be handwritten on paper, photographed, and embedded in your submission (). 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:
- Will restricting to one sex change which species pairs differ, compared with your full-data result in Part 9?
- 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):
- State H₀ and Hₐ for your one-sex test.
- 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.
- 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_mminstead ofbody_mass_gand see whether the species separate the same way.
Getting unstuck
could not find function "Anova"→library(car), and mind the capital A (baseanova()is different).could not find function "emmeans"→library(emmeans).cldnot found →install.packages("multcomp"), thenmultcomp::cld(...).- Cheat sheets — https://posit.co/resources/cheatsheets/
💡 Key idea: Nothing about
aov(),Anova(), oremmeans()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.