# Load all packages at the top ----------------------------
library(readxl) # reading Excel files
library(tidyverse) # data manipulation + ggplot2
library(janitor) # clean_names()
library(car) # Levene's test for variance equalityLecture 10 — T-Tests II: Running & Reporting the Test
Running Welch’s t-test, reading the output, and writing it up
Running Welch’s two-sample t-test, decoding every line of the output, making a formal decision, and writing a proper scientific results sentence.
Where we left off (Lecture 09 — T-Tests I)
- H₀: \(\mu_{shady} = \mu_{sunny}\) · Hₐ: \(\mu_{shady} \neq \mu_{sunny}\) (two-tailed, α = 0.05)
- Normality checked — histogram + Shapiro-Wilk, roughly bell-shaped for ~25 per group
- Variance checked — Levene’s test run (we use Welch’s either way)
- Decision: proceed with Welch’s two-sample t-test
✅ Key idea from Lecture 09
Every box is checked. Today we actually run the test, decode what R hands back, and turn it into a sentence a reader can trust.
Goals for Today
- Run Welch’s two-sample t-test in R
- Read every line of the
t.test()output - Decide: reject or fail to reject H₀
- Visualize the result with the test statistics embedded
- Report results in proper scientific writing format
🖐 Try it yourself
By the end you will have a complete, reportable t-test result and a publication-style figure to go with it — whatever the test says.
References:
- 📖 Whitlock & Schluter, Ch. 4 — Estimating with Uncertainty
- 📖 R4DS §3
Both are in the course readings/ folder.
Today’s naming:
- test objects →
_model - plots →
_plot
How to Use These Slides — Predict · Type · Run
This lecture runs in two short chunks. After each chunk you switch to the activity and type the code yourself into your R script.
For every code block, do three things:
- Predict — before it runs, say what you think the output will be
- Type it out by hand — do not copy-paste
- Run it and compare to your prediction
✅ Why bother?
Guessing “above or below 0.05” before you see t.test() output forces you to reason about the test instead of just reading a number off the screen — that habit is what makes a p-value meaningful instead of magic.
Load Libraries and Data
# Load the leaf data from the data folder -----------------
leaf_df <- read_excel("data/2026_09_03_data_sci_leaf_area.xlsx") %>%
clean_names()Quick Recap of Our Assumption Checks
# Rerun the assumption checks quickly for reference -----
recap_df <- leaf_df %>%
group_by(shade) %>%
summarize(
n = sum(!is.na(mass_g)),
mean_mass = round(mean(mass_g, na.rm = TRUE), 3),
sd_mass = round(sd(mass_g, na.rm = TRUE), 3),
se_mass = round(sd_mass / sqrt(n), 3)
)
recap_df# A tibble: 2 × 5
shade n mean_mass sd_mass se_mass
<chr> <int> <dbl> <dbl> <dbl>
1 shady 28 0.523 0.148 0.028
2 sunny 25 0.505 0.21 0.042
leveneTest(mass_g ~ shade, data = leaf_df)Levene's Test for Homogeneity of Variance (center = median)
Df F value Pr(>F)
group 1 2.9667 0.09105 .
51
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
- Both groups: roughly normal (Lecture 09)
- Levene’s: recorded, but does not change our test choice
- We proceed with Welch’s (
var.equal = FALSE)
If your Activity 09 numbers don’t match exactly, that’s fine — small rounding differences don’t change the conclusion.
🧩 Chunk 1 of 2 · Run & Interpret
We will cover: running Welch’s t.test(), reading every line of the output, and making a formal reject / fail-to-reject decision.
Step 4 — Run the Welch’s t-Test
🔮 Predict first: Write down a guess for the p-value before you run t.test() — above or below 0.05? Then see how close you were.
# Welch's two-sample t-test — unequal variance ---------
leaf_ttest_model <- t.test(
mass_g ~ shade, # formula: response ~ grouping variable
data = leaf_df,
var.equal = FALSE, # Welch's — no pooled variance
alternative = "two.sided" # two-tailed test
)
leaf_ttest_model
Welch Two Sample t-test
data: mass_g by shade
t = 0.35579, df = 42.527, p-value = 0.7238
alternative hypothesis: true difference in means between group shady and group sunny is not equal to 0
95 percent confidence interval:
-0.08392774 0.11987117
sample estimates:
mean in group shady mean in group sunny
0.5230357 0.5050640
Key arguments:
| argument | what it does |
|---|---|
mass_g ~ shade |
compare mass_g between levels of shade |
var.equal = FALSE |
use Welch’s correction |
alternative = "two.sided" |
two-tailed test |
The result is a model object. We decode each line of the output on the next slide.
Reading the t.test() Output
# Extract individual values from the model object ------
cat("t-statistic :", round(leaf_ttest_model$statistic, 3), "\n")t-statistic : 0.356
cat("df (Welch) :", round(leaf_ttest_model$parameter, 2), "\n")df (Welch) : 42.53
cat("p-value :", signif(leaf_ttest_model$p.value, 3), "\n")p-value : 0.724
cat(
"95% CI :",
round(leaf_ttest_model$conf.int[1], 3),
"to",
round(leaf_ttest_model$conf.int[2], 3),
"g\n"
)95% CI : -0.084 to 0.12 g
cat("Mean shady :", round(leaf_ttest_model$estimate[1], 3), "g\n")Mean shady : 0.523 g
cat("Mean sunny :", round(leaf_ttest_model$estimate[2], 3), "g\n")Mean sunny : 0.505 g
Line by line:
| output | meaning |
|---|---|
t |
our calculated t-statistic |
df |
Welch-Satterthwaite df (non-integer is normal!) |
p-value |
probability of this result if H₀ is true |
95% CI |
plausible range for the true difference in means |
mean of shady/sunny |
the two group means |
If the 95% CI includes zero, “no difference” is one of the plausible values — consistent with a p above 0.05.
Step 5 — Make a Decision
# State the decision formally --------------------------
# if/else: R checks the condition and runs one of two blocks
# We will learn these formally in the functions lecture
p_val <- leaf_ttest_model$p.value
alpha <- 0.05
if (p_val < alpha) {
cat("p =", signif(p_val, 3), "< α =", alpha, "\n")
cat("Decision: REJECT H₀\n")
cat("Conclusion: mean leaf mass differs between sides.\n")
} else {
cat("p =", signif(p_val, 3), ">= α =", alpha, "\n")
cat("Decision: FAIL TO REJECT H₀\n")
cat("Conclusion: no evidence that mean leaf mass differs between sides.\n")
}p = 0.724 >= α = 0.05
Decision: FAIL TO REJECT H₀
Conclusion: no evidence that mean leaf mass differs between sides.
What the p-value means:
If H₀ were true (no real difference), we would get a t-statistic at least this large p × 100% of the time just by chance.
We set α = 0.05 before the test. If p < α, we reject H₀; otherwise we fail to reject it.
What “fail to reject” does NOT mean:
- It is not proof that H₀ is true
- It means this sample did not provide enough evidence of a difference
- Report the effect size (difference in means) and CI alongside p
→ ACTIVITY 10 Parts 1–3 now
🧩 Chunk 2 of 2 · Visualize & Report
We will cover: embedding the test result in a boxplot and writing a scientific results sentence.
Visualize the Result
# Final boxplot with all points -----------------------
leaf_box_10_plot <- leaf_df %>%
ggplot(aes(x = shade, y = mass_g, fill = shade)) +
geom_boxplot(alpha = 0.5, outlier.shape = NA) +
geom_point(
position = position_jitter(width = 0.15, seed = 42),
alpha = 0.6,
size = 2.5
) +
labs(
title = "Leaf Mass by Shade",
subtitle = paste0(
"Welch's t-test: t = ",
round(leaf_ttest_model$statistic, 2),
", p = ",
signif(leaf_ttest_model$p.value, 2)
),
x = "Shade",
y = "Leaf Mass (g)"
) +
theme_minimal() +
theme(legend.position = "none")
leaf_box_10_plot
Including the test result in the figure:
subtitlepulls t and p directly from the model object- R fills in the actual numbers automatically
- If you re-run with new data, the subtitle updates
Embedding test statistics in the plot subtitle saves you from copying numbers by hand — and avoids typos in your report.
How to Report Results in Science
# Build the results sentence from the model object -----
t_val <- round(leaf_ttest_model$statistic, 2)
df_val <- round(leaf_ttest_model$parameter, 1)
p_val2 <- signif(leaf_ttest_model$p.value, 2)
heavier <- if (leaf_ttest_model$estimate[1] > leaf_ttest_model$estimate[2]) "shady" else "sunny"
lighter <- if (heavier == "shady") "sunny" else "shady"
cat("--- Results sentence ---\n")--- Results sentence ---
if (leaf_ttest_model$p.value < 0.05) {
cat(heavier, "-side leaves were significantly heavier than ",
lighter, "-side leaves\n", sep = "")
} else {
cat("Mean leaf mass did not differ significantly between sides\n")
}Mean leaf mass did not differ significantly between sides
cat("(Welch's t-test: t(", df_val, ") = ", t_val, ", p = ", p_val2, ").\n", sep = "")(Welch's t-test: t(42.5) = 0.36, p = 0.72).
cat(
"Mean ± SE: shady =",
round(leaf_ttest_model$estimate[1], 3),
"±",
round(recap_df$se_mass[recap_df$shade == "shady"], 3),
"g, sunny =",
round(leaf_ttest_model$estimate[2], 3),
"±",
round(recap_df$se_mass[recap_df$shade == "sunny"], 3),
"g.\n"
)Mean ± SE: shady = 0.523 ± 0.028 g, sunny = 0.505 ± 0.042 g.
Standard format:
“[Finding] (Welch’s t-test: t(df) = X.XX, p = X.XXX; mean ± SE: group1 = X.X ± X.X units, group2 = X.X ± X.X units).”
Always include:
- Test type (Welch’s t-test)
- t-statistic and df
- Exact p-value (or < 0.001)
- Group means ± SE with units
A non-significant result is still a result — report it plainly (“did not differ significantly”), never as “proved there is no difference.”
Never report “p = 0.000” — write “p < 0.001” instead.
Save Your Plot
# Save the plot to the figures folder ------------------
ggsave(
"figures/leaf_mass_boxplot_ttest.png",
plot = leaf_box_10_plot,
width = 5,
height = 5,
units = "in",
dpi = 300
)Your plot is now in your figures/ folder with the test statistics embedded in the subtitle — ready for a lab report or paper.
dpi = 300 is publication quality. Use it for any figure that will appear in a paper, poster, or formal report.
→ ACTIVITY 10 Parts 4–5 now
What We Learned Today
Concepts:
- Steps 4–5 of the five-step framework — run, interpret, report
- Reading output — t, df, p, CI, group means
- A results sentence needs: test name, t, df, p, means ± SE, units
- A non-significant result is reported plainly — not as proof of “no difference”
R skills:
t.test(y ~ group, data, var.equal = FALSE, alternative = "two.sided")model$statistic,model$p.value,model$conf.int— extract resultspaste0(...)inlabs(subtitle = )— embed test statistics in figures
References:
- 📖 Whitlock & Schluter, Ch. 4 — Estimating with Uncertainty
- 📖 R4DS §3
Up next — Lecture 11:
- Linear regression — predicting leaf area from paper tracing mass
- Building a calibration curve with
lm() - R², residuals, and assumption checking