Activity 04 - GGPlot II
More geoms, zooming safely, and picking a theme
Hands-on companion to the GGPlot II lecture. Map shape alongside color, add a trend line, compare coord_cartesian() to xlim()/ylim(), and pick and save a themed figure.
More Geoms, Coordinates, and Themes
Recap from Activity 03
- Piped
leaf_dfstraight intoggplot() - Assigned exact colors with
scale_fill_manual()/scale_color_manual() - Split plots with
facet_wrap()andfacet_grid() - Built a mean ± SE plot with
stat_summary()
Today’s Objectives
- Map shape alongside color, and add a
geom_smooth()trend line - Compare
coord_cartesian()toxlim()/ylim() - Pick a built-in theme and adjust it with
theme() - Save a plot as both PNG and PDF
How this activity works
You are building one R script this whole class, and you turn it in. Create it now:
scripts/04_ggplot_2.R.
- Every line you run goes in that script — not in the Console, not typed into this page. Type it, don’t paste it.
- Start every code chunk in your script with a short
#comment that says what it does. The comments are part of the grade.- The top of your script, in this order: a title comment, then your
library()calls, then the line that loads the data intoleaf_df.- Code marked ▶ Run this is typed into your script exactly as shown. Code marked ✏️ Your turn is a change you make in that same script and run.
🔮 Predict before you run
Before you run any ▶ Run this block, cover the output and predict what R will draw. Predicting is what turns “I saw it on a slide” into “I can write it.”
Part 1 · Load libraries and data
Same leaf file as Activity 02–03 — it should already be in your project’s data/ folder. If not: 2026_09_03_data_sci_leaf_area.xlsx → put it in data/.
▶ Type this at the very top of scripts/04_ggplot_2.R:
# ---- Activity 04: GGPlot II ------------------------------
# your name, today's date
# ---- Libraries -------------------------------------------
library(readxl) # read Excel files
library(tidyverse) # dplyr + ggplot2
library(janitor) # clean_names()# ---- Load data ------------------------------------------
leaf_df <- read_excel("data/2026_09_03_data_sci_leaf_area.xlsx") %>%
clean_names()
glimpse(leaf_df) # look at the data right after loadingPart 2 · Shape, color, and a trend line
🔮 Predict first: We map
color = shadeandshape = shadeto the same variable. One legend or two?
▶ Run this:
# scatter of mass vs petiole length, one color + shape per shade
leaf_df %>%
ggplot(aes(x = petiole_mm, y = mass_g, color = shade, shape = shade)) +
geom_point(size = 2.5, alpha = 0.8) +
scale_color_manual(values = c("sunny" = "goldenrod", "shady" = "forestgreen")) +
labs(
title = "Leaf Mass vs. Petiole Length",
x = "Petiole Length (mm)", y = "Leaf Mass (g)",
color = "Shade", shape = "Shade"
) +
theme_minimal()✏️ Your turn — in your script: Add a geom_smooth(method = "lm") layer to the plot above (no color/fill needed — let it default). Run it.
Does the trend line slope up or down?
Part 3 · coord_cartesian() vs. xlim()/ylim()
▶ Run this — Version A, coord_cartesian():
# zoom the view WITHOUT dropping data: coord_cartesian()
leaf_df %>%
ggplot(aes(x = petiole_mm, y = mass_g)) +
geom_point(alpha = 0.6, color = "grey40") +
geom_smooth(method = "lm", color = "tomato", fill = "tomato", alpha = 0.15) +
coord_cartesian(xlim = c(50, 70), ylim = c(0.4, 0.6)) +
labs(title = "Zoomed with coord_cartesian()", x = "Petiole Length (mm)", y = "Leaf Mass (g)") +
theme_minimal()🔮 Predict first: Will the trend line in Version B look the same as Version A, or different? Why?
▶ Run this — Version B, xlim()/ylim():
# zoom the view by DELETING rows outside the window: xlim() / ylim()
leaf_df %>%
ggplot(aes(x = petiole_mm, y = mass_g)) +
geom_point(alpha = 0.6, color = "grey40") +
geom_smooth(method = "lm", color = "tomato", fill = "tomato", alpha = 0.15) +
xlim(50, 70) +
ylim(0.4, 0.6) +
labs(title = "Zoomed with xlim()/ylim()", x = "Petiole Length (mm)", y = "Leaf Mass (g)") +
theme_minimal()✏️ Your turn: Compare the two trend lines. Are they the same? Explain why, in terms of what each function does to the data before the line is fit.
Same or different?
Why:
Part 4 · Pick a theme
▶ Run this, trying each theme in turn:
# build the base plot once, then add each theme on top
leaf_plot <- leaf_df %>%
ggplot(aes(x = shade, y = mass_g, fill = shade)) +
geom_boxplot(alpha = 0.6, outlier.shape = NA) +
scale_fill_manual(values = c("sunny" = "goldenrod", "shady" = "forestgreen")) +
labs(x = "Shade", y = "Leaf Mass (g)", title = "Leaf Mass by Shade")
leaf_plot + theme_grey()
leaf_plot + theme_bw()
leaf_plot + theme_minimal()
leaf_plot + theme_classic()
leaf_plot + theme_light()Which theme do you like best for this plot, and why?
Adjust it with theme()
▶ Run this:
# start from theme_classic(), then override a few individual elements
leaf_plot_final <- leaf_plot +
theme_classic() +
theme(
legend.position = "none",
axis.text = element_text(size = 12),
axis.title = element_text(size = 13)
)
leaf_plot_final✏️ Your turn — in your script: Change axis.text to size = 16 and run it.
Too big, too small, or just right?
Part 5 · Save in two formats
▶ Run this:
# PNG for slides and web, PDF for print / vector editing
ggsave("figures/leaf_mass_theme.png",
plot = leaf_plot_final, width = 5, height = 5,
units = "in", dpi = 300)
ggsave("figures/leaf_mass_theme.pdf",
plot = leaf_plot_final, width = 5, height = 5, units = "in")Open both files. When would you send a colleague the .pdf instead of the .png?
Part 6 · Review and checkpoint
At this point you should be able to:
✏️ Your turn — before you move on: Run your entire script top to bottom with Ctrl/Cmd + Shift + Enter (Source). Does it complete without errors?
Ran cleanly? Y / N
If not, what error appeared:
What your project folder should contain
tree_project/
├── data/
│ └── 2026_09_03_data_sci_leaf_area.xlsx <- never edit this
├── figures/
│ ├── leaf_mass_theme.png <- Part 5
│ └── leaf_mass_theme.pdf <- Part 5
└── scripts/
└── 04_ggplot_2.R <- your script (turn this in)
Extension — out of class (~30–40 min)
Add this to the bottom of scripts/04_ggplot_2.R and turn it in with the rest. Put your written answers in # comments right under the code they go with. In class you zoomed a fixed window on mass_g vs petiole_mm. Now the variable pair and the window are yours.
E1 · Your own zoom window (4 pts)
- Pick your own pair of numeric variables from
leaf_dfthat class did not use together (notmass_gvspetiole_mm).thickness_mmis available. - Look at the range of your x-variable (
range()/summary()), then pick a zoom window that cuts the data roughly in half — a real cut, not a guess. - Build the
coord_cartesian()version and thexlim()version of a scatter withgeom_smooth(method = "lm"), exactly like the in-class Part 3. - Run
nrow(leaf_df), thenfilter()leaf_dfto your window and checknrow()again — that’s the row countxlim()is silently using.
Record your exact window and both row counts in comments.
E2 · Predict, then check (3 pts)
Before running E1: in a comment, write your variable pair and window, and predict — will the xlim() trend line be the same slope as the coord_cartesian() one, steeper, or shallower? Also predict, as a number, how many rows land in your window. Run the code, then write how close your row-count guess was and whether the trend lines differed as predicted.
E3 · Explain it, using YOUR numbers (3 pts)
- Using your before/after row counts, explain why
xlim()gave a different line thancoord_cartesian()for your window. If the counts came out equal, explain how that can happen. - In your own words, why is it dangerous that
xlim()/ylim()delete rows silently? Give a realistic case where that silent deletion leads to a wrong conclusion. - Name the theme you chose in Part 4 and say, in a sentence or two, why you’d pick it over
theme_grey()for a figure in a report.
Getting unstuck
- Read the error message out loud. R usually names the line and the problem.
- Line looks wrong after zooming? Check whether you used
xlim()/ylim()(deletes data) instead ofcoord_cartesian()(keeps it). - Legend didn’t merge?
colorandshape(or the matchinglabs()titles) must reference the exact same variable name. - Cheat sheets — https://posit.co/resources/cheatsheets/
- Bring the exact error (copy-paste it) to class, Canvas, or office hours.
End of the GGplot II activity. Next: wrangling with filter, select, mutate, and arrange.