One Quarto file: read the data, plot it, describe it, test it — Word, PDF, and slides
2026-10-01
read_excel() + clean_names()stat_summary(), histogramsgroup_by() + summary_stats(): n, mean, SD, SE, 95% CI.R scriptNote
✅ Key idea
You already know every piece of a real analysis. Today we put all of it — plus a t-test — into one document a human can read.
Tip
🖐 You will hand in
leaf_report.qmd, leaf_report.docx, and leaf_report.pdf.
Tools today:
broom — turns test output into a table (installed with tidyverse)Get the starter file:
References:
This lecture runs in four short chunks. After each chunk you switch to the activity and fill in your own report.
For every snippet, do three things:
Note
✅ Why bother? (the evidence)
We will cover: the pain of script-plus-copy-paste, literate programming, reproducibility, and where to save a .qmd so your paths just work.
Tip
🖐 After this chunk: Activity Part 1 (save the starter file at the top of your project and Render it).
Your .R script is great for running code. But when the lab report is due you have to:
Warning
⚠️ Watch out!
Every hand-copied number is a chance for a typo. Change one data point and the whole report is out of date.
Note
📊 Coming from Excel?
This is the same pain as pasting a chart into Word, then editing the spreadsheet and forgetting to update the chart.
Literate programming — write the story and the code in the same file.
Change the data, Render again, and every table and figure rebuilds itself. That is what scientists mean by reproducible.
Note
📖 New word
Quarto = the tool that turns one plain-text .qmd file into a polished document.
Note
✅ Key idea
A script does the analysis. A Quarto document does the analysis and explains it — and rebuilds itself on demand.
.qmd go? — the top of your projectQuarto runs the code from the folder the .qmd is saved in. Save it at the top of the project and these paths work exactly as they do in your scripts:
Note
✅ Key idea
.qmd at the top + short paths into subfolders = no here package, no ../. Same paths in the Console, in your scripts, and in the report.
Warning
⚠️ Watch out!
Save it inside documents/ and "data/..." breaks — Quarto would look for documents/data/. You would need "../data/..." everywhere. Don’t.
🛑 Do Activity Part 1 now
Download leaf_report_skeleton.qmd, save it at the top of your project as leaf_report.qmd, and press Render before you change anything.
.qmdWe will cover: YAML (and the one line that hides all the code), Markdown, code chunks, and the chunk options you will use all term.
Tip
🖐 After this chunk: Activity Parts 2–4 (YAML, Markdown, chunk options).
Every Quarto document has the same three ingredients:
You already read these every day — this whole lecture is a .qmd file.
Note
📖 New word
.qmd = a Quarto markdown file. Plain text you can open anywhere.
Note
🔮 Predict first: The execute: block below says echo: true. What changes in the whole document if you switch it to false?
---
title: "Leaf Mass on the Sunny and Shady Sides of a Tree"
author: "Your Name"
date: today
format: # one file -> three documents
docx:
toc: true
number-sections: true
pdf:
toc: true
number-sections: true
fig-pos: "H"
revealjs:
scrollable: true
smaller: true
execute: # defaults for EVERY chunk
echo: true # false = hide ALL the code at once
warning: false
message: false
---format: — which documents to buildexecute: — settings for every chunk; one line turns all the code on or offTip
One switch
Write with echo: true so you can see your code. Flip to echo: false for the version you hand in.
Warning
⚠️ Watch out!
YAML is space-sensitive: two spaces, never tabs.
Note
🔮 Predict first: Before it renders, sketch what this becomes — which line is a big heading, which are bullets, what turns bold?
# heading, ## smaller heading**bold**, *italic*, - bullets<!-- --> a note to yourself — the skeleton is full of themNote
📊 Coming from Word?
**bold** is just the keyboard version of clicking the B button. Same result, no mouse.
Note
🎞 Bonus
In the slide version, every # becomes a section slide and every ## becomes a slide.
A code chunk is fenced R that actually runs when you render:
```{r} — closing fence ```#| are chunk options# comment saying what it does — same rule as your scripts| Option | What it does | Example |
|---|---|---|
label |
Names the chunk. fig- / tbl- at the front numbers it as a Figure / Table |
#| label: fig-boxplot |
echo |
Show (true) or hide (false) the code |
#| echo: false |
eval |
Run the code (true) or only show it (false) |
#| eval: false |
include |
false runs the code but hides both code and output |
#| include: false |
output |
false hides the results but keeps the code |
#| output: false |
warning / message |
Show or hide warnings / package messages | #| message: false |
fig-width / fig-height |
Figure size in inches | #| fig-width: 5 |
fig-cap |
Figure caption | #| fig-cap: "Leaf mass by side" |
tbl-cap |
Table caption | #| tbl-cap: "Summary statistics" |
tbl-colwidths |
Column widths in percent — tidy Word tables | #| tbl-colwidths: [30, 70] |
🛑 Do Activity Parts 2–4 now
Put your name in the YAML and try the echo switch; write the Introduction and Methods in Markdown; then use the reference card to hide the setup chunk. Predict each Render before you press it.
We will cover: the three plots you already know — box plot, mean ± SE, histogram — as named chunks with numbered captions, then a summary statistics table that looks right in Word.
Tip
🖐 Your skeleton already has a plain, working version of every chunk below. You add the finishing touches — colors, labels, theme_regular(), table formatting — in Activity Parts 5–6.
The setup chunk above it (already in your skeleton) loads readxl, tidyverse, janitor, broom, and source()s the theme file and summary_stats().
Warning
⚠️ Watch out!
Quarto runs the file top to bottom in a fresh R session — not whatever is sitting in your Console. Libraries and data must load in a chunk above anything that uses them.
```{r}
#| label: fig-boxplot
#| output-location: column
#| fig-cap: "Leaf mass by side; each point is one leaf."
#| fig-width: 5
#| fig-height: 4
# box plot with raw points on top ----
leaf_df %>%
ggplot(aes(x = shade, y = mass_g, fill = shade)) +
geom_boxplot(alpha = 0.6, outlier.shape = NA) +
geom_jitter(width = 0.15, alpha = 0.6)
```fig- — what does the caption start with?labs(), theme_regular(), no legend```{r}
#| label: fig-mean-se
#| output-location: column
#| fig-cap: "Mean leaf mass (± 1 SE) by side."
#| fig-width: 5
#| fig-height: 4
# mean +/- standard error plot ----
leaf_df %>%
ggplot(aes(x = shade, y = mass_g, color = shade)) +
geom_jitter(width = 0.15, alpha = 0.35, size = 2) +
stat_summary(fun = mean, geom = "point", size = 4) +
stat_summary(fun.data = mean_se, geom = "errorbar",
width = 0.15, linewidth = 0.9)
```color, not fill```{r}
#| label: fig-histogram
#| output-location: column
#| fig-cap: "Distribution of leaf mass on each side."
#| fig-width: 6
#| fig-height: 3.5
# one histogram per side ----
leaf_df %>%
ggplot(aes(x = mass_g, fill = shade)) +
geom_histogram(binwidth = 0.05, color = "white") +
facet_wrap(~shade)
```6 × 3.5) because it has two panelslabs(), theme_regular(), no legend```{r}
#| label: tbl-summary
#| tbl-cap: "Summary statistics for leaf mass (g) by side."
#| tbl-colwidths: [12, 7, 12, 15, 12, 12, 15, 15]
# summary statistics by side, printed as a table ----
mass_stats_df <- leaf_df %>%
group_by(shade) %>%
summary_stats(mass_g)
mass_stats_df %>%
knitr::kable(
digits = 3,
col.names = c("Side", "n", "Mean", "Variance",
"SD", "SE", "CI low", "CI high"))
```| Side | n | Mean | Variance | SD | SE | CI low | CI high |
|---|---|---|---|---|---|---|---|
| shady | 28 | 0.523 | 0.022 | 0.148 | 0.028 | 0.466 | 0.580 |
| sunny | 25 | 0.505 | 0.044 | 0.210 | 0.042 | 0.418 | 0.592 |
summary_stats() — same helper as Activity 06knitr::kable() — data frame → real tabledigits = 3 — rounds the whole tablecol.names = — readable headers, one per columntbl-colwidths: — eight columns, eight percentsIn your text write @tbl-summary and Quarto prints “Table 1” for you.
tbl-colwidths? Tables in WordWithout widths, Word guesses — and it often guesses badly: a fat first column and the last column squashed until the numbers wrap.
n) less, headers like Variance moreNote
✅ Key idea
kable() makes the table; tbl-colwidths makes it line up in Word and PDF. Same numbers, readable layout.
🛑 Do Activity Parts 5–6 now
Render the plain plots and table first, then finish them: colors, axis labels, theme_regular(), no legend, and digits + col.names in kable(). Render after each one.
We will cover: a Welch’s two-sample t-test (you met the t-test in intro bio), a clean results table, then hiding the code and rendering Word, PDF, and slides.
Tip
🖐 After this chunk: Activity Parts 7–9 (t-test, render all three and hand in, then play).
Question: is the difference between the two means bigger than we’d expect by chance?
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
mass_g ~ shade — “mass by shade”: number on the left, two groups on the rightvar.equal = FALSE — Welch’s version: does not assume the two sides have the same varianceNote
📖 Why Welch’s?
It is safe whether or not the variances match, so it is R’s default and a sensible first choice. Next lecture takes it apart properly.
```{r}
#| label: tbl-ttest
#| tbl-cap: "Welch's t-test of leaf mass by side of the tree."
#| tbl-colwidths: [20, 20, 15, 15, 15, 15]
# the same result as a clean table ----
leaf_ttest %>%
tidy() %>%
select(estimate1, estimate2, statistic, parameter,
p.value, conf.low) %>%
knitr::kable(
digits = 3,
col.names = c("Mean shady", "Mean sunny", "t",
"df", "p-value", "CI low"))
```| Mean shady | Mean sunny | t | df | p-value | CI low |
|---|---|---|---|---|---|
| 0.523 | 0.505 | 0.356 | 42.527 | 0.724 | -0.084 |
tidy() (from broom) turns the printout into a data frameselect() + kable() — tools you already haveNote
✅ Reading it
p is well above 0.05, so we fail to reject the null: these data show no difference in mean leaf mass between sides — just what the overlapping error bars predicted. That is a real result; report it honestly.
For the version you hand in, flip the switch in the YAML:
…and hide the raw t-test printout — the table already has those numbers. The chunk still runs, so leaf_ttest still exists:
Note
✅ Key idea
execute: in the YAML = the default for every chunk. A chunk’s own #| line = the exception for that one chunk.
The Render button previews one format. To build everything listed under format:, use the Terminal tab (not the Console):
| File | What it is |
|---|---|
leaf_report.docx |
Word report — hand in |
leaf_report.pdf |
PDF report — hand in |
leaf_report.html |
RevealJS slide show — press F for full screen |
These work in any Quarto presentation — they only change the slides, never the Word or PDF.
| Control | Where it goes | What it does |
|---|---|---|
output-location: column |
chunk #| line |
code on the left, result on the right |
output-location: slide |
chunk #| line |
result gets its own slide after the code |
output-location: fragment |
chunk #| line |
result appears on the next click |
fig-width / fig-height |
chunk #| line |
a shorter figure fits under more text |
echo: false |
chunk or execute: |
hide the code — just show the result |
smaller: true |
under revealjs: |
smaller text on every slide |
scrollable: true |
under revealjs: |
a long slide scrolls instead of being cut off |
## Title {.smaller} |
on one heading | smaller text on that slide only |
:::: columns |
in the Markdown | put any two things side by side |
Tip
Put output-location: column under revealjs: in the YAML and every chunk in your slides gets code-left, plot-right.
🛑 Do Activity Parts 7–8 now
Read the Welch’s t-test, finish its table, write a short Discussion, set echo: false, then quarto render leaf_report.qmd in the Terminal. Hand in the .qmd, .docx, and .pdf — then play (Activity Part 9).
.qmd at the top of the project so data/... paths just workechoNote
✅ Key idea
From here on, every analysis can be a document that rebuilds itself from the raw data.
Tip
🧰 Keep leaf_report.qmd
It is your template for every analysis this term: copy it, change the data file and column names, rewrite the text, Render.
Up next — T-Tests I: