# Libraries
library(tidyverse)
library(ggtext)
# Setting up the data ----
<- palmerpenguins::penguins %>%
penguins mutate(banana_quantity = case_when(species == "Adelie" & island == "Biscoe" ~ 1,
== "Adelie" & island == "Dream" ~ 0.6,
species == "Adelie" & island == "Torgersen" ~ 0,
species TRUE ~ 1))
<- palmerpenguins::penguins %>%
penguin_summaries group_by(species) %>%
summarise(bill_depth_mm = mean(bill_depth_mm, na.rm = TRUE),
bill_length_mm = mean(bill_length_mm, na.rm = TRUE)) %>%
mutate(commentary = case_when(species == "Adelie" ~
"The Adelie penguins tried varying the amount of banana in the mix. Turns out, even a hint of green banana is detrimental to yumminess!",
== "Gentoo" ~
species "Over-ripe bananas and typically shorter baking times.",
TRUE ~ "Ripe bananas and slightly longer cooking times."))
# We are renaming variables here so that they match what's inside our main aes() call at the start of our plot
<- palmerpenguins::penguins_raw %>%
penguin_highlights ::clean_names() %>%
janitorrename(bill_depth_mm = culmen_depth_mm,
bill_length_mm = culmen_length_mm) %>%
filter(bill_length_mm %in% c(max(bill_length_mm, na.rm = TRUE),
sort(bill_length_mm, decreasing = TRUE)[2],
min(bill_length_mm, na.rm = TRUE))) %>%
arrange(bill_length_mm) %>%
mutate(species = gsub("(.) (.*)", "\\1", species),
commentary = case_when(bill_length_mm == max(bill_length_mm) ~
paste0("Our star baker is **", individual_id,
"**, a ", species, " from ", island,
". Congratulations, ", individual_id, "!"),
== sort(bill_length_mm, decreasing = TRUE)[2] ~
bill_length_mm paste0("Our runner up is a ", species,
" from ", island, ": **", individual_id,
"**, proving that ripe and over-ripe bananas are both good options!"),
TRUE ~ paste0("**", individual_id,
"**, did not have a good baking day. The combination of short cooking time and green bananas probably didn't help!")),
label_x = c(15, 18.15, 16.45),
label_y = c(34, 57, 59),
h_align = case_when(label_x < bill_depth_mm ~ 1,
TRUE ~ 0),
arrow_x_end = case_when(label_x < bill_depth_mm ~ bill_depth_mm - 0.1,
TRUE ~ bill_depth_mm + 0.1),
arrow_y_end = case_when(label_y < bill_length_mm ~ bill_length_mm - 0.1,
TRUE ~ bill_length_mm + 0.1))
# Establish a colour scheme ----
<- list("Adelie" = "#89973d",
banana_colours "Chinstrap" = "#e8b92f",
"Gentoo" = "#a45e41")
# Build on it to add light and dark text colours
<- monochromeR::generate_palette(
dark_text $Chinstrap, "go_darker",
banana_coloursn_colours = 2)[2]
<- monochromeR::generate_palette(
light_text "go_lighter",
dark_text, n_colours = 3)[2]
<- list("Adelie" = "#89973d",
banana_colours "Chinstrap" = "#e8b92f",
"Gentoo" = "#a45e41",
"dark_text" = dark_text,
"light_text" = light_text)
# Plot it! ----
ggplot(penguins,
# aesthetics that apply to all sets of data
aes(x = bill_depth_mm,
y = bill_length_mm,
colour = species)) +
geom_point(aes(alpha = banana_quantity)) +
geom_textbox(data = penguin_summaries,
aes(label = paste0("**Team ", species, "**",
"<br><span style = \"color:",
$light_text,
banana_colours"\">", commentary, "</span>")),
family = "DM Sans",
size = 4,
width = unit(12, "line"),
alpha = 0.9,
box.colour = NA) +
geom_textbox(data = penguin_highlights,
aes(label = commentary,
x = label_x,
y = label_y,
halign = h_align,
hjust = h_align),
show.legend = FALSE,
family = "DM Sans",
size = 3.2,
fill = NA,
box.colour = NA) +
geom_curve(data = penguin_highlights,
aes(x = label_x, xend = arrow_x_end,
y = label_y, yend = arrow_y_end),
arrow = arrow(length = unit(0.1, "cm")),
curvature = list(0.15),
alpha = 0.5) +
scale_colour_manual(values = banana_colours) +
scale_alpha(range = c(0.2, 0.9)) +
labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:",
$Chinstrap, "\">ripe</span> or<br><span style=\"color:",
banana_colours$Gentoo, "\">over-ripe</span> bananas"),
banana_colourssubtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.",
x = "Baking time",
y = "Yumminess",
caption = "Data from {palmerpenguins}; misused for illustration purposes.") +
guides("none") +
theme_minimal(base_size = 12) +
theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text,
lineheight = 1.2),
plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
panel.grid = element_line(colour = "#F6F6F5"),
axis.text = element_text(size = 6),
plot.caption = element_text(size = 6),
legend.position="none")
Level Up Your Plots: Enhance the story telling capabilities of your datavisualisations
RUG @ HDSI - September, 2022
It was an absolute pleasure discussing these design tips and coding tricks with the Harvard Data Science Initiative R User group. I particularly enjoyed the Q&A at the end where we discussed how best to apply these tips in the context of academic publishing.
Here’s a recording of the talk, a copy of the slides, the code used to create the final plot, and the transformation of the plot from a basic plot to one that tells a memorable story.
Recording
Slides
Full plot code
Plot transformation
Reuse
Citation
For attribution, please cite this work as:
“Level Up Your Plots: Enhance the Story Telling Capabilities of
Your Datavisualisations.” 2022. September 29, 2022. https://www.cararthompson.com/talks/hdsi_rug.