Activity 09 — T-Tests I: Setting Up the Test
Hypotheses and checking assumptions before we test
Hands-on companion to T-Tests I: state hypotheses, check normality (histograms, box plot + QQ plot, Shapiro-Wilk) and variance (Levene’s), then run Welch’s t-test. Students hand in the same analysis, written themselves, on sugar maple leaves.
In-class Activity 9: Setting Up the Test
Today’s Objectives
- State null and alternate hypotheses before running any test
- Check normality — stacked histograms, a box plot next to a QQ plot, and the Shapiro-Wilk test
- Check equal variance — Levene’s test
- Run Welch’s t-test (we decode the output next lecture)
- Skeleton script:
09_t_tests_1_skeleton.R→ save it asscripts/09_t_tests_1.R. - Leaf data:
2026_09_03_data_sci_leaf_area.xlsx→data/(you should already have it). - Sugar maple data:
2026_09_02_sugar_maple_leaf_area.xlsx→data/.
The skeleton has two parts:
- Part A — in class (leaf data). The code is already written. Run it chunk by chunk as you work through Parts 1–8 below, and type your answers in the QA boxes.
- Part B — hand in (sugar maple data). Only the questions are there. You write the code for the assumption checks by copying the matching Part A chunk and changing what needs changing. Answer the QB boxes. (The sugar maple t-tests come next lecture.) See Part 9.
Answer boxes look like this — type after ANSWER: and start every line with #:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# QA2. How far apart are the two means?
# ANSWER: shady is about 0.02 g heavier
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #Keep the plots plain today — no colors, labels, or themes needed. The goal is to read them, not polish them.
🧩 Chunk 1 — Setup & recap (after lecture Chunk 1)
Parts 1–2: load the data and recompute the descriptive stats.
Part 1 · Load libraries and data
▶ Run this (already at the top of your skeleton — it loads both data sets):
# Load libraries ---------------------------------------
library(readxl) # read Excel files
library(tidyverse) # dplyr + ggplot2
library(janitor) # clean_names()
library(car) # Levene's test
# Load the leaf data (Part A) --------------------------
leaf_df <-
read_excel("data/2026_09_03_data_sci_leaf_area.xlsx") %>%
clean_names()
# Load the sugar maple data (Part B) -------------------
maple_df <-
read_excel("data/2026_09_02_sugar_maple_leaf_area.xlsx") %>%
clean_names()
glimpse(leaf_df)
glimpse(maple_df)⚠️ Watch out! If R says
could not find function "leveneTest", you haven’t loadedlibrary(car). Install it once withinstall.packages("car").
Part 2 · Quick descriptive stats
🔮 Predict first: guess the mean leaf mass for each side and which side has more spread (bigger SD).
▶ Run this:
# Mean and SD of leaf mass for each side ---------------
leaf_df %>%
group_by(shade) %>%
summarize(
n = sum(!is.na(mass_g)),
mean = mean(mass_g, na.rm = TRUE),
sd = sd(mass_g, na.rm = TRUE)
)✏️ QA2: How far apart are the two means? Which side has the bigger SD?
🧩 Chunk 2 — Hypotheses & assumptions (after lecture Chunk 2)
Parts 3–7: state your hypotheses, then check normality and variance — before you test.
Part 3 · State your hypotheses FIRST
✏️ QA3: Write H₀ and Hₐ for leaf mass, in words and in symbols (use μ for “mean”). Say whether the test is one- or two-tailed, and what α is. There is no code for this part — just the answer box.
Part 4 · Normality — stacked histograms
facet_wrap(~shade, ncol = 1) stacks the two histograms on top of each other so they share one x-axis — you can compare where each group sits and how wide it spreads.
▶ Run this:
# Histograms of leaf mass, one on top of the other ------
leaf_df %>%
ggplot(aes(x = mass_g)) +
geom_histogram(binwidth = 0.05) +
facet_wrap(~shade, ncol = 1)✏️ QA4: For each side — roughly bell-shaped, or lopsided with a long tail? Which side spreads wider?
Part 5 · Normality — box plot and QQ plot
A QQ plot is new, so start with something you know.
Step 1: the box plot
▶ Run this:
# Box plot of leaf mass by side ------------------------
leaf_df %>%
ggplot(aes(x = shade, y = mass_g)) +
geom_boxplot()The box plot shows three quantiles — values that a given percent of the leaves fall below:
| Part of the box | Quantile | Meaning |
|---|---|---|
| Bottom of box | Q1 | 25% of leaves are lighter |
| Middle line | Median | 50% of leaves are lighter |
| Top of box | Q3 | 75% of leaves are lighter |
In a normal (bell-shaped) distribution the median sits in the middle of the box and the two whiskers are about the same length. A long whisker on one side = a long tail on that side.
Step 2: the QQ plot
A QQ plot uses every leaf as a quantile, not just three. R:
- sorts the leaves from lightest to heaviest (the y-axis),
- works out where each leaf would be if the data were perfectly normal (the x-axis),
- plots one dot per leaf.
If the data are normal, the dots fall on a straight line.
🔮 Predict first: From the box plot, which side’s dots do you expect to bend away from the line — and at the top or the bottom?
▶ Run this:
# QQ plot of leaf mass for each side --------------------
leaf_df %>%
ggplot(aes(sample = mass_g)) +
stat_qq() +
stat_qq_line() +
facet_wrap(~shade)- A QQ plot uses
aes(sample = ...)— notx =ory =. stat_qq()draws the dots;stat_qq_line()draws the “perfectly normal” line.
How to read it:
| What the dots do | What it means |
|---|---|
| Follow the line | Normal — good |
| Curve up at the right end | Long tail of big values (right-skewed) |
| Curve down at the left end | Long tail of small values (left-skewed) |
| One or two dots far off at an end | Outliers |
A little wiggle at the very ends is normal. Look for a clear bend.
✏️ QA5: For each side, do the dots follow the line? Where do they bend away? Does that match the long whisker in the box plot and the tail in the histogram?
Part 6 · Normality — Shapiro-Wilk test
The plots are your main evidence. Shapiro-Wilk puts a p-value on it.
- H₀: the data are normal
- p > 0.05 → no evidence against normality
- p < 0.05 → evidence the data are not normal
🔮 Predict first: From your QQ plots, will p be above or below 0.05 for each side?
▶ Run this:
# Shapiro-Wilk p-value for each side -------------------
leaf_df %>%
group_by(shade) %>%
summarize(shapiro_p = shapiro.test(mass_g)$p.value)This is the same group_by() + summarize() you use for means — it runs the test once per side. $p.value pulls just the p-value out of the test result.
✏️ QA6: Record p for each side. Does the test agree with your QQ plots?
⚠️ Watch out! A p just under 0.05 with a QQ plot that is mostly on the line is a mild problem, not a disaster — the t-test handles small departures from normality.
Part 7 · Equal variance — Levene’s test
- H₀: the two groups have equal variance (spread)
- p > 0.05 → spreads not significantly different
- p < 0.05 → spreads differ
🔮 Predict first: From your SDs in Part 2 and the stacked histograms, will p be above or below 0.05?
▶ Run this:
# Levene's test for equal variance ---------------------
leveneTest(mass_g ~ shade, data = leaf_df)✏️ QA7: Record F and p. We use Welch’s t-test either way — why is it the safe choice?
🧩 Chunk 3 — Run the test (after lecture Chunk 3)
Part 8 · Run Welch’s t-test
🔮 Predict first: above or below 0.05?
▶ Run this:
# Welch's two-sample t-test ----------------------------
leaf_model <- t.test(mass_g ~ shade, data = leaf_df,
var.equal = FALSE)
leaf_model✏️ QA8: Record t, df, and the p-value. Do you reject H₀ or fail to reject it? We go through every line of this output next lecture.
Save your script. That’s Part A done — on to Part B.
Part 9 · Your turn: sugar maple (hand in)
Start in class, finish before next class. This is Part B of your skeleton and it is what gets graded. You are checking the assumptions only — the sugar maple t-tests (two-sample and paired) are T-Tests II.
Every step in Part B matches a step in Part A. For each # TODO:, copy the matching Part A chunk, paste it under the TODO, and change what needs changing. Things that are different:
- the data frame is
maple_df - the groups are in a column called
side(notshade) - sugar maple leaves are much heavier (about 2–5 g) — a
binwidthof 0.05 will give you a comb of skinny bars; pick a better one - B8 changes the variable too: repeat the checks for leaf area,
total_area_cm2 - B9 looks at the design — how many leaves came from each tree and side
- B10 is your decision: based on your checks, is a t-test OK for mass and for area?
Give every chunk its own # comment, and answer every QB box.
⚠️ Watch out! If you get
object 'shade' not found, you changed the data frame but not the group column.
When you’re done, run the whole script top to bottom (Ctrl/Cmd + Shift + Enter) — it must run with no errors — and hand in scripts/09_t_tests_1.R.
End of Activity 09. Next: Activity 10 — T-Tests II: reading the output, writing a results sentence, and paired t-tests.