Resources
Setup
R
library(ggplot2)
gap <- read.csv("rawdata/gapminder.csv")
Base R Plots

R has built-in plotting functions. They're quick but limited.

R
plot(x = gap$year, gap$lifeExp)
plot(x = gap$year, gap$lifeExp,
     xlab = "Year", ylab = "Life Expectancy (yrs)")
hist(gap$lifeExp)

# Saving base R plots is awkward
jpeg("plots/lifeExp_histogram.jpg")
hist(gap$lifeExp, breaks = 30)
dev.off()
ggplot2 — The Grammar of Graphics

ggplot2 builds plots in layers. Every plot starts with data and aesthetics, then adds geoms (geometric objects).

R
# Start with data — empty canvas
ggplot(gap)

# Add aesthetics (x/y variables)
ggplot(gap, aes(x = gdpPercap, y = lifeExp))

# Add a geom layer — now you have points
ggplot(gap, aes(x = gdpPercap, y = lifeExp)) +
  geom_point()

# Boxplot
ggplot(gap, aes(x = lifeExp, y = continent)) +
  geom_boxplot()
Challenge 1

Use str(gap) to explore the data. Find another variable to plot. Try year vs. lifeExp. Notice the problem with boxplot — fix it with group = year or as.factor(year).

Grouping by Year — Two Fixes

R
# Fix 1: tell ggplot to group by year
ggplot(gap, aes(x = year, y = lifeExp, group = year)) +
  geom_boxplot()

# Fix 2: coerce year to a factor
gap %>%
  mutate(year = as.factor(year)) %>%
  ggplot(aes(x = year, y = lifeExp)) +
    geom_boxplot()
Colors, Sizes, and Shapes
R
# Set a fixed color
ggplot(gap, aes(x = gdpPercap, y = lifeExp)) +
  geom_point(color = "blue", alpha = 0.5)

# Map color to a variable (inside aes)
ggplot(gap, aes(x = gdpPercap, y = lifeExp, color = continent)) +
  geom_point(alpha = 0.5)

# ggsci palette (install.packages("ggsci") first)
library(ggsci)
ggplot(gap, aes(x = year, y = lifeExp, group = country, color = continent)) +
  geom_line() +
  scale_color_aaas()

# Manual colors (use ColorBrewer to pick)
ggplot(gap, aes(x = year, y = lifeExp, group = country, color = continent)) +
  geom_line() +
  scale_color_manual(values = c("#4a1486", "#6a51a3", "#807dba", "#9e9ac8", "#bcbddc"))

# Shapes and size
ggplot(gap, aes(x = gdpPercap, y = lifeExp, color = continent, shape = continent)) +
  geom_point(alpha = 0.5, size = 2) +
  scale_color_nejm()
Multiple Geom Layers
R
# Layer boxplot + jitter
ggplot(gap, aes(x = lifeExp, y = continent)) +
  geom_boxplot(outlier.alpha = 0) +
  geom_jitter(alpha = 0.5, height = 0.3, aes(color = year))

# Labels and themes
ggplot(gap, aes(x = lifeExp, y = continent)) +
  geom_boxplot(outlier.alpha = 0) +
  geom_jitter(alpha = 0.5, height = 0.3, aes(color = year)) +
  labs(x = "Life Expectancy (yrs)", y = "Continent", title = "LifeExp by Continent") +
  theme_classic()
Practice: try different themes

Pick any plot and test theme_bw(), theme_minimal(), theme_classic(), theme_light(). Pick your favorite.

Ask AI to Customize Your Plot AI

Tweaking ggplot2 details (axis breaks, legend position, margins) is tedious to look up. Ask AI instead — describe exactly what you want.

Try this prompt in Claude or ChatGPT
I'm making a ggplot2 plot in R. Here's my current code: ggplot(gap, aes(x = lifeExp, y = continent)) + geom_boxplot(outlier.alpha = 0) + geom_jitter(alpha = 0.5, height = 0.3, aes(color = year)) + labs(x = "Life Expectancy (yrs)", y = "Continent", title = "LifeExp by Continent") + theme_classic() Please modify it to: 1. Set x-axis breaks at every 10 years from 30 to 90 2. Rotate the y-axis label to be horizontal 3. Move the legend to the bottom of the plot Explain what each change does.

The Finished Plot with AI Customizations

R
Plot1 <- ggplot(gap, aes(x = lifeExp, y = continent)) +
  geom_boxplot(outlier.alpha = 0) +
  geom_jitter(alpha = 0.5, height = 0.3, aes(color = year)) +
  scale_x_continuous(breaks = seq(30, 90, by = 10), limits = c(30, 90)) +
  theme(axis.title.y = element_text(angle = 0, vjust = 0.5)) +
  labs(x = "Life Expectancy (yrs)", y = "Continent", title = "LifeExp by Continent") +
  theme_classic()

Plot1

ggsave(Plot1, file = "plots/lifeByContinent.png", width = 5, height = 5, units = "in")
ggsave(Plot1, file = "plots/lifeByContinent.pdf", width = 5, height = 5, units = "in")
Facets and Multi-Panel Plots
R
# Facet wrap — one panel per continent
ggplot(gap, aes(x = gdpPercap, y = lifeExp, color = continent)) +
  geom_point() +
  scale_x_log10() +
  scale_color_nejm() +
  theme_bw() +
  facet_wrap(~ continent)

# Use AI to fix the legend position in faceted plots
ggplot(gap, aes(x = gdpPercap, y = lifeExp, color = continent)) +
  geom_point() +
  scale_x_log10() +
  scale_color_nejm() +
  theme_bw() +
  facet_wrap(~ continent) +
  theme(
    legend.position = c(1, 0),
    legend.justification = c("right", "bottom"),
    plot.margin = unit(c(5.5, 5.5, 5.5, 5.5), "mm")
  )

# Highlight a specific country with a second geom layer
dataUS <- gap %>% filter(country == "United States")

plot2 <- ggplot(gap, aes(x = gdpPercap, y = lifeExp, color = continent)) +
  geom_point() +
  scale_x_log10() +
  facet_wrap(~ continent) +
  geom_point(data = dataUS, aes(x = gdpPercap, y = lifeExp), color = "black")

ggsave(plot2, file = "plots/LifeGdpFacet.png", width = 5, height = 5, units = "in")
Combining Plots with patchwork
R
install.packages("patchwork")
library(patchwork)

multipanel <- Plot1 + plot2

ggsave(multipanel,
       file = "plots/combo.png",
       width = 8, height = 5, units = "in")
ggsave(multipanel,
       file = "plots/combo.pdf",
       width = 8, height = 5, units = "in")
AI for Plot Customization — Keep Going AI

At this point, AI is faster than documentation for tweaking plots. Describe what you want in plain English.

General-purpose ggplot2 customization prompt
I'm working in R with ggplot2. Here's my current plot code: [paste your code here] I want to change the following: - [describe what you want to change] Please modify the code and explain what each change does.
What to ask AI about

Axis labels and tick marks · Legend position and appearance · Font sizes and families · Color palette changes · Adding text or annotation labels · Adjusting margins · Changing background or gridlines. Describe what you see vs. what you want — AI will figure out the right theme() arguments.