Lesson 3 of 3

Annotate & Compose

In Lessons 1 and 2, Maya the baker took her three branches, Downtown, Riverside and the big Airport shop, split the crowded chart into small multiples, and restyled it with a clean theme and colourblind-safe colours. The chart is readable and on-brand. Now she has to present it: on Friday she pitches two investors, and a chart that needs her standing beside it to explain it is a chart that fails the moment she sits down.

This lesson is about making a plot speak for itself, then stitching several plots into one figure. Three moves: mark the values that matter with reference lines and notes, label the points so each one is named, and compose several charts into a single slide. The scatter below is Maya's raw canvas, foot traffic against revenue for all three branches. By the end you will turn it, and its siblings, into a briefing.

By the end of this lesson you will be able to:

  • Add reference lines and free-floating notes with geom_hline() and annotate()
  • Label crowded points without them overlapping, using ggrepel
  • Stitch several plots into one captioned figure with patchwork

Prerequisites: you can build a basic ggplot and map a column inside aes(), ggplot(data, aes(...)) + geom_*() (from Data Visualization with ggplot2, and Lessons 1 and 2 of this course, Facets and Scales and Themes, Colour and Accessibility). Every new function is defined as it appears.

The idea

Two kinds of layer: encode, and explain

Every ggplot you have built so far is made of geoms, layers that turn rows of data into ink: geom_point() puts a dot at each row, geom_line() connects them. An annotation is a different kind of layer. It does not read your data row by row; it draws a fixed mark at a position you name: a horizontal target line, a note, a circle around one point. Geoms answer "what does the data say"; annotations answer "what should the reader notice".

Each lesson starts in a fresh R session, so let us put Maya's week back on the page. Run this block once and the rest of the lesson builds on it:

RInteractive R
library(ggplot2) days <- c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") # One row per branch per day: 3 branches x 7 days = 21 rows. sales <- data.frame( branch = rep(c("Downtown", "Riverside", "Airport"), each = 7), day = factor(rep(days, times = 3), levels = days), foot_traffic = c(150, 165, 180, 205, 240, 300, 260, # Downtown 95, 110, 120, 140, 175, 220, 190, # Riverside 520, 560, 610, 680, 760, 980, 880), # Airport revenue = c(300, 330, 360, 430, 520, 690, 600, # Downtown 190, 210, 240, 280, 360, 470, 410, # Riverside 2100, 2300, 2520, 2900, 3300, 4200, 3700) # Airport ) # Pull out one branch's week and draw it plainly, no explanation yet. downtown <- subset(sales, branch == "Downtown") # group = 1 tells geom_line to join all 7 days into one line; without it, # a categorical x-axis (the day names) leaves the points unconnected. ggplot(downtown, aes(day, revenue, group = 1)) + geom_line(colour = "#1f7a55", linewidth = 1) + geom_point(size = 2)

  

That is Downtown's daily revenue, a clean line. But it says nothing about what matters: what counts as a good day, where the break-even line sits, which day was best. Those are exactly the things an annotation layer adds.