Level up your plots

RUG @ HDSI | September 29th, 2022

Hi there đź‘‹ !

đź‘© Cara Thompson

👩‍💻 Psychology PhD %>% Analysis of postgraduate medical examinations %>% Freelance data consultant specialising in dataviz and “enhanced” reproducible outputs

đź’™ Helping others maximise the impact of their expertise


Today’s goal

To equip you with some design tips and coding tricks to enhance the story-telling capabilities of your plots.

  • Illustrate ways in which we can use colour and text to communicate a story more clearly
  • Share the code behind these tips, building on intro to {ggplot2} and introducing {ggtext}
  • Provide resources you can revisit in your own time (slides available at link below, data from {palmerpenguins})
  • Give feedback via Q&A about your own plots

But first, please suspend all disbelief…

The Great Penguin Bake Off

The penguins had a baking competition to see which species could make the best banana loaf. Each species was given bananas of a different level of ripeness.

The Great Penguin Bake Off

The penguins had a baking competition to see which species could make the best banana loaf. Each species was given bananas of a different level of ripeness.

The Great Penguin Bake Off

The Adelie penguins decided to experiment with different quantities of banana in their mix. Each island chose a different quantity.

The Great Penguin Bake Off

The Adelie penguins decided to experiment with different quantities of banana in their mix. Each island chose a different quantity.

The Great Penguin Bake Off

The penguins also baked their cakes for different amounts of time. Here are the mean durations per species. Which species left their cakes in the oven for longest?

The Great Penguin Bake Off

The penguins also baked their cakes for different amounts of time. Here are the mean durations per species. Which species left their cakes in the oven for longest?

Seven tips for storytelling

#1 - Use colour (and transparency) purposefully

#2 - Use intuitive orientations

Seven tips for storytelling

#1 - Use colour (and transparency) purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

Make it easy for the readers to remember what is what.

But my research isn’t about bananas!

machine <- "#061939"
human <- "#e25470"
monochromeR::generate_palette(machine, 
                              blend_colour = human, 
                              n_colours = 3, 
                              view_palette = TRUE)




Let’s get coding!

Setting up our basic plot

We first let’s modify the data, so that we have banana quantities to visualise!

library(palmerpenguins)
library(tidyverse)

penguins <- palmerpenguins::penguins %>%
  mutate(banana_quantity = 
           case_when(
             species == "Adelie" & 
               island == "Biscoe" ~ 1,
             species == "Adelie" & 
               island == "Dream" ~ 0.6,
             species == "Adelie" & 
               island == "Torgersen" ~ 0,
             TRUE ~ 1))

penguins %>% 
  select(species, island, banana_quantity) %>%
  distinct()
# A tibble: 5 x 3
  species   island    banana_quantity
  <fct>     <fct>               <dbl>
1 Adelie    Torgersen             0  
2 Adelie    Biscoe                1  
3 Adelie    Dream                 0.6
4 Gentoo    Biscoe                1  
5 Chinstrap Dream                 1  

Setting up our basic plot

Ripeness & quantities, baking duration (bill depth), yumminess (bill length)

ggplot(penguins,
       aes(x = bill_depth_mm,
           y = bill_length_mm,
           colour = species)) +
  geom_point(aes(alpha = banana_quantity))

Setting up our basic plot

Ripeness & quantities, baking duration (bill depth), yumminess (bill length)

ggplot(penguins,
       aes(x = bill_depth_mm,
           y = bill_length_mm,
           colour = species)) +
  geom_point(aes(alpha = banana_quantity)) +
  theme_minimal()

Setting up our basic plot

Ripeness & quantities, baking duration (bill depth), yumminess (bill length)

ggplot(penguins,
       aes(x = bill_depth_mm,
           y = bill_length_mm,
           colour = species)) +
  geom_point(aes(alpha = banana_quantity)) +
  theme_minimal(base_size = 16)

Setting up our basic plot

Ripeness & quantities, baking duration (bill depth), yumminess (bill length)

ggplot(penguins,
       aes(x = bill_depth_mm,
           y = bill_length_mm,
           colour = species)) +
  geom_point(aes(alpha = banana_quantity)) +
  scale_alpha(range = c(0.2, 1)) +
  theme_minimal(base_size = 16)

Setting up our basic plot

Ripeness & quantities, baking duration (bill depth), yumminess (bill length)

basic_plot <- ggplot(penguins,
               aes(x = bill_depth_mm,
                   y = bill_length_mm,
                   colour = species)) +
  geom_point(aes(alpha = banana_quantity)) +
  labs(title = "Banana loaf tastes best when baked with ripe or over-ripe bananas",
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness.
The Adelie penguins were given unripe bananas, Gentoos were given over-ripe 
bananas and Chinstraps were given yellow bananas.
Each penguin was left to choose their own cooking time.",
       x = "Baking time",
       y = "Yumminess",
       caption = "Data from {palmerpenguins}; misused for illustration purposes.") +
  scale_alpha(range = c(0.2, 1)) +
  theme_minimal(base_size = 16)

basic_plot

Setting up our basic plot

Ripeness & quantities, baking duration (bill depth), yumminess (bill length)

basic_plot <- ggplot(penguins,
               aes(x = bill_depth_mm,
                   y = bill_length_mm,
                   colour = species)) +
  geom_point(aes(alpha = banana_quantity)) +
  labs(title = "Banana loaf tastes best when baked with ripe or over-ripe bananas",
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness.
The Adelie penguins were given unripe bananas, Gentoos were given over-ripe 
bananas and Chinstraps were given yellow bananas.
Each penguin was left to choose their own cooking time.",
       x = "Baking time",
       y = "Yumminess",
       caption = "Data from {palmerpenguins}; misused for illustration purposes.") +
  scale_alpha(range = c(0.2, 1)) +
  theme_minimal(base_size = 12)

basic_plot

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

Adelie = Unripe, Chinstrap = Ripe, Gentoo = Over-ripe

Transparency (banana quantity) taken care of by alpha:

ggplot(penguins,
               aes(x = bill_depth_mm,
                   y = bill_length_mm,
                   colour = species)) +
  geom_point(aes(alpha = banana_quantity))

#1 - Use colour purposefully

The quick fix…

ggplot(penguins, 
       aes(x = bill_depth_mm,
           y = bill_length_mm,
           colour = species)) +
  geom_point() +
  theme_minimal() +
  scale_colour_manual(values = c("#89973d", 
                                 "#e8b92f", 
                                 "#a45e41"))

#1 - Use colour purposefully

… might be a dangerous shortcut!

ggplot(penguins %>% 
         # Oh, that should be a factor, 
         # let me fix that for you!
         mutate(species = 
                  factor(species, 
                         levels = c("Chinstrap", 
                                    "Gentoo", 
                                    "Adelie"))), 
       aes(x = bill_depth_mm,
           y = bill_length_mm,
           colour = species)) +
  geom_point() +
  theme_minimal() +
  scale_colour_manual(values = c("#89973d", 
                                 "#e8b92f", 
                                 "#a45e41"))

#1 - Use colour purposefully

Create a named list!

banana_colours <- list("Adelie" = "#89973d",
                       "Chinstrap" = "#e8b92f",
                       "Gentoo" = "#a45e41")

ggplot(penguins,
       aes(x = bill_depth_mm,
           y = bill_length_mm,
           colour = species)) +
  geom_point() +
  theme_minimal() +
  scale_colour_manual(values = banana_colours)

#1 - Use colour purposefully

Create a named list!

banana_colours <- list("Adelie" = "#89973d",
                       "Chinstrap" = "#e8b92f",
                       "Gentoo" = "#a45e41")

ggplot(penguins %>% 
         # Oh, that should be a factor, 
         # let me fix that for you!
         mutate(species = 
                  factor(species, 
                         levels = c("Chinstrap", 
                                    "Gentoo", 
                                    "Adelie"))), 
       aes(x = bill_depth_mm,
           y = bill_length_mm,
           colour = species)) +
  geom_point() +
  theme_minimal() +
  scale_colour_manual(values = banana_colours)

#1 - Use colour purposefully

Create a named list!

banana_colours <- list("Adelie" = "#89973d",
                       "Chinstrap" = "#e8b92f",
                       "Gentoo" = "#a45e41")

basic_plot +
  scale_colour_manual(values = banana_colours)

#1 - Use colour purposefully

Choosing colours is tricky!

  • Take inspiration from other dataviz / art you like
  • Start from the colour wheel and read around how best to use it!

#1 - Use colour purposefully

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#2 - Use intuitive orientations

duration_plot

#2 - Use intuitive orientations

duration_plot +
  coord_flip()

#2 - Use intuitive orientations

We’ve done this already!


ggplot(penguins,
       aes(x = bill_depth_mm,  # Duration
           y = bill_length_mm, # Yumminess
           colour = species))

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#3 - Add colour to text

Say hello to 📦 {ggtext}

#3 - Add colour to text

Say hello to 📦 {ggtext}

library(ggtext)

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">ripe</span> or <span style=\"color:",
    banana_colours$Gentoo, "\">over-ripe</span> bananas"))

[1] "Banana loaf tastes best when baked with <span style=\"color:#e8b92f\">ripe</span> or <span style=\"color:#a45e41\">over-ripe</span> bananas"

#3 - Add colour to text

Say hello to 📦 {ggtext}

library(ggtext)

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">ripe</span> or <span style=\"color:",
    banana_colours$Gentoo, "\">over-ripe</span> bananas")) +
  theme(plot.title = element_markdown())

#3 - Add colour to text

Say hello to 📦 {ggtext}

library(ggtext)

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">**ripe**</span> or <span style=\"color:",
    banana_colours$Gentoo, "\">**over-ripe**</span> bananas")) +
  theme(plot.title = element_markdown())

#3 - Add colour to text

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#4 - Add text hierarchy

#4 - Add text hierarchy

Two more colours for our palette

dark_text <- monochromeR::generate_palette(
  banana_colours$Chinstrap, "go_darker",
  n_colours = 2)[2]

light_text <-  monochromeR::generate_palette(
  dark_text, "go_lighter",
  n_colours = 3)[2]

banana_colours <- list("Adelie" = "#89973d",
                       "Chinstrap" = "#e8b92f",
                       "Gentoo" = "#a45e41",
                       "dark_text" = dark_text,
                       "light_text" = light_text)

monochromeR::view_palette(banana_colours)

#4 - Add text hierarchy

Where we were…

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">**ripe**</span> or <span style=\"color:",
    banana_colours$Gentoo, "\">**over-ripe**</span> bananas")) +
  theme(plot.title = element_markdown())

#4 - Add text hierarchy

Where we were…

basic_plot +
  scale_colour_manual(values = banana_colours, 
                      limits = force) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">**ripe**</span> or <span style=\"color:",
    banana_colours$Gentoo, "\">**over-ripe**</span> bananas")) +
  theme(plot.title = element_markdown())

#4 - Add text hierarchy

Add a base colour and font for text

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">**ripe**</span> or <span style=\"color:",
    banana_colours$Gentoo, "\">**over-ripe**</span> bananas")) +
  theme(text = element_text(family = "DM Sans", 
                            colour = banana_colours$light_text),
        plot.title = element_markdown())

#4 - Add text hierarchy

Override it in the title

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">**ripe**</span> or <span style=\"color:",
    banana_colours$Gentoo, "\">**over-ripe**</span> bananas")) +
  theme(text = element_text(family = "DM Sans", 
                            colour = banana_colours$light_text),
        plot.title = element_markdown(colour = banana_colours$dark_text))

#4 - Add text hierarchy

Change the font and size of the title

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">**ripe**</span> or <span style=\"color:",
    banana_colours$Gentoo, "\">**over-ripe**</span> bananas")) +
  theme(text = element_text(family = "DM Sans", 
                            colour = banana_colours$light_text),
        plot.title = element_markdown(
          size = 18, 
          family = "Poppins", 
          colour = banana_colours$dark_text, 
          face = "bold")
        )

#4 - Add text hierarchy

Add a line<br>eak

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">**ripe**</span> or<br><span style=\"color:",
    banana_colours$Gentoo, "\">**over-ripe**</span> bananas")) +
  theme(text = element_text(family = "DM Sans", 
                            colour = banana_colours$light_text),
        plot.title = element_markdown(
          size = 18, 
          family = "Poppins", 
          colour = banana_colours$dark_text, 
          face = "bold")
        )

#4 - Add text hierarchy

Apply the same principle to the axes and caption

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0(
    "Banana loaf tastes best when baked with ",
    "<span style=\"color:", banana_colours$Chinstrap, 
    "\">ripe</span> or<br><span style=\"color:",
    banana_colours$Gentoo, "\">over-ripe</span> bananas")) +
  theme(text = element_text(family = "DM Sans", 
                            colour = banana_colours$light_text),
        plot.title = element_markdown(
          size = 18, 
          family = "Poppins", 
          colour = banana_colours$dark_text, 
          face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6))

#4 - Add text hierarchy

Getting custom fonts to work can be frustrating!

Install fonts locally + {ragg} + {systemfonts} + {textshaping} + Set graphics device to “AGG” + 🤞


#4 - Add text hierarchy

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#5 - Reduce unnecessary eye movement

#5 - Reduce unnecessary eye movement

Combine all the above to create text boxes instead of a legend!

# Create a new tibble
penguin_summaries <- palmerpenguins::penguins %>%
  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!",
                                species == "Gentoo" ~
                                  "Over-ripe bananas and typically shorter baking times.",
                                TRUE ~ "Ripe bananas and slightly longer cooking times."))

#5 - Reduce unnecessary eye movement

Combine all the above to create text boxes instead of a legend!

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas")) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6))

#5 - Reduce unnecessary eye movement

Combine all the above to create text boxes instead of a legend!

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas")) +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0(
                 "**Team ", species, "**",
                 "<br><span style = \"color:",
                 banana_colours$light_text,
                 "\">", commentary, "</span>"))) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6))

#5 - Reduce unnecessary eye movement

Combine all the above to create text boxes instead of a legend!

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas")) +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0(
                 "**Team ", species, "**",
                 "<br><span style = \"color:",
                 banana_colours$light_text,
                 "\">", commentary, "</span>")),
               family = "DM Sans",
               size = 3.5,
               width = unit(9, "line"),
               alpha = 0.9,
               box.colour = NA) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6))

#5 - Reduce unnecessary eye movement

Combine all the above to create text boxes instead of a legend!

basic_plot +
  scale_colour_manual(values = banana_colours, limits = force) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness.
Each penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0(
                 "**Team ", species, "**",
                 "<br><span style = \"color:",
                 banana_colours$light_text,
                 "\">", commentary, "</span>")),
               family = "DM Sans",
               size = 3.5,
               width = unit(9, "line"),
               alpha = 0.9,
               box.colour = NA) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        legend.position = "none")

#5 - Reduce unnecessary eye movement

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#6 - Highlight important data points

First, a bit of text manipulation!

#6 - Highlight important data points

First, a bit of text manipulation!

penguin_highlights <- palmerpenguins::penguins_raw %>%
  # Housekeeping
  janitor::clean_names() %>%
  rename(bill_depth_mm = culmen_depth_mm,
         bill_length_mm = culmen_length_mm) %>%
  # Find star baker, runner up and lowest score
  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))) 

#6 - Highlight important data points

First, a bit of text manipulation!

penguin_highlights <- palmerpenguins::penguins_raw %>%
  janitor::clean_names() %>%
  rename(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))) %>%
  # More housekeeping
  mutate(species = gsub("(.) (.*)", "\\1", species))


raw_species <- unique(palmerpenguins::penguins_raw$Species)
raw_species
[1] "Adelie Penguin (Pygoscelis adeliae)"      
[2] "Gentoo penguin (Pygoscelis papua)"        
[3] "Chinstrap penguin (Pygoscelis antarctica)"
gsub("(.) (.*)", "\\1", raw_species)
[1] "Adelie"    "Gentoo"    "Chinstrap"

#6 - Highlight important data points

First, a bit of text manipulation!

penguin_highlights <- palmerpenguins::penguins_raw %>%
  janitor::clean_names() %>%
  rename(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))) %>%
  mutate(species = gsub("(.) (.*)", "\\1", species),
         # Add commentary!
         commentary = case_when(
           bill_length_mm == max(bill_length_mm) ~
             paste0("Our star baker is **", individual_id,
                    "**, a ", species, " from ", island,
                    ". Congratulations, ", individual_id, "!"),
           bill_length_mm == sort(bill_length_mm, decreasing = TRUE)[2] ~
             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!")))

#6 - Highlight important data points

Next, let’s work out where we want our labels…

#6 - Highlight important data points

… and add box coordinates and text alignment to our data

penguin_highlights <- palmerpenguins::penguins_raw %>%
  janitor::clean_names() %>%
  rename(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))) %>%
  # more housekeeping!
  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, "!"),
           bill_length_mm == sort(bill_length_mm, decreasing = TRUE)[2] ~
             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!")),
         # Add label and arrow coordinates
         label_x = c(15, 18.15, 16.45),
         label_y = c(34, 57, 59),
         left_to_right = 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))

#6 - Highlight important data points

Let’s add the annotations…

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
               family = "DM Sans", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        legend.position = "none")

#6 - Highlight important data points

Let’s add the annotations…

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
               family = "DM Sans", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  geom_textbox(data = penguin_highlights,
               aes(label = commentary,
                   x = label_x,
                   y = label_y,
                   hjust = left_to_right),
               family = "DM Sans", 
               size = 3,
               fill = NA,
               box.colour = NA) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        legend.position = "none")

#6 - Highlight important data points

… using arrows and alignments to emphasise the story

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
               family = "DM Sans", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  geom_textbox(data = penguin_highlights,
               aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right),
               family = "DM Sans", size = 3, 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,
                 hjust = left_to_right),
             arrow = arrow(length = unit(0.1, "cm")),
             curvature = list(0.15),
             alpha = 0.5) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        legend.position = "none")

#6 - Highlight important data points

… using arrows and alignments to emphasise the story

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
               family = "DM Sans", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  geom_textbox(data = penguin_highlights,
               aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right,
                   halign = left_to_right),
               family = "DM Sans", size = 3, 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) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        legend.position = "none")

#6 - Highlight important data points

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#1 - Use colour purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

#7 - Give everything some space to breathe



#7 - Give everything some space to breathe

Increase lineheight, reduce distractions

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
               family = "DM Sans", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  geom_textbox(data = penguin_highlights,
               aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right, halign = left_to_right),
               family = "DM Sans", size = 3, 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) +
  theme(text = element_text(family = "DM Sans", colour = banana_colours$light_text),
        plot.title = element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        legend.position = "none")

#7 - Give everything some space to breathe

Increase lineheight, reduce distractions

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
              aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
               family = "DM Sans", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  geom_textbox(data = penguin_highlights,
               aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right, halign = left_to_right),
               family = "DM Sans", size = 3, 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) +
  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"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        legend.position = "none")

#7 - Give everything some space to breathe

Increase lineheight, reduce distractions

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
               family = "DM Sans", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  geom_textbox(data = penguin_highlights,
               aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right, halign = left_to_right),
               family = "DM Sans", size = 3, 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) +
  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"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        panel.grid = element_line(colour = "#F6F6F5"),
        legend.position = "none")

#7 - Give everything some space to breathe

Increase lineheight, reduce distractions, check everything fits!

basic_plot +
  scale_colour_manual(values = banana_colours) +
  labs(title = paste0("Banana loaf tastes best when baked with <span style=\"color:", banana_colours$Chinstrap, "\">ripe</span> or<br><span style=\"color:", banana_colours$Gentoo, "\">over-ripe</span> bananas"),
       subtitle = "The Palmer Penguins carried out an experiment using bananas of different ripeness. \nEach penguin was left to choose their own cooking time.") +
  geom_textbox(data = penguin_summaries,
               aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
               family = "DM Sans", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  geom_textbox(data = penguin_highlights,
               aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right, halign = left_to_right),
               family = "DM Sans", size = 3, 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_x_continuous(expand = expansion(mult = c(0.2, 0.02))) +
  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"),
        axis.text = element_text(size = 6),
        plot.caption = element_text(size = 6),
        panel.grid = element_line(colour = "#F6F6F5"),
        legend.position = "none")

#7 - Give everything some space to breathe

Seven tips for storytelling

#1 - Use colour (and transparency) purposefully

#2 - Use intuitive orientations

#3 - Add colour to text to help orient readers

#4 - Use colours and fonts to add text hierarchy

#5 - Reduce unnecessary eye movement

#6 - Highlight important data points

#7 - Give everything some space to breathe

Seven tips for storytelling

Seven tips for storytelling

Seven tips for storytelling

Over to you!