Level up your plots

NHS-R 2022 | Online Workshop | 4th November 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 design tips and coding tricks to enhance the story-telling capabilities of your plots.

A mix of talk, live coding and exercises

  • Part 1: Using colour and orientations to make the story easier to remember
  • Part 2: Adding colour and hierarchy to text, to keep the main thing the main thing
  • Part 3: Annotating the plot to highlight key information
  • Bonus track: Sources of inspiration + Q&A about your own plots

Introductions

  • I’m… and I work in…
  • I mostly use R for…
  • My favourite type of plot is…
  • The thing I find most challenging when creating a plot is…

Today’s goal

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

A mix of talk, live coding and exercises

  • Part 1: Using colour and orientations to make the story easier to remember
  • Part 2: Adding colour and hierarchy to text, to keep the main thing the main thing
  • Part 3: Annotating the data to highlight key information
  • Bonus track: Sources of inspiration + 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 to level up your plots

#1 - Use colour (and transparency) purposefully

#2 - Use intuitive orientations

Seven tips to level up your plots

#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

Part 1 - Colour and orientation

#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!

A bit of housekeeping

  • Using Tidyverse style (mostly!)
  • package::function() rather than loading whole libraries (helps us see where everything comes from!)
  • library(tidyverse)
  • Apply the principles to your own plots during each exercise break - shout if you get stuck!
  • For each part, there’s a script in reference-scripts/ to show where we got to

Setting up our basic plot

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

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 = 12)

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),
              breaks = c(0.1, 0.5, 1)) +
  theme_minimal(base_size = 12)

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),
              breaks = c(0.1, 0.5, 1)) +
  theme_minimal(base_size = 12)

basic_plot

#1 - Use colour purposefully

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

  • Look for photos of bananas
  • Extract colours using imagecolorpicker.com
  • Banana quantity taken care of by alpha (transparency)

#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!

Here are a few starting points…

  • Your department brand guidelines
  • A photo + something like imagecolorpicker.com to pick out colours
  • Take inspiration from other dataviz / art you like
  • Google images and “[whatever you like] palette”
  • Or… start from the colour wheel and read around how best to use it
  • Using a tool like paletton.com makes it easier!

#1 - Use colour purposefully

#1 - Use colour purposefully

#1 - Use colour purposefully

  • Think about accessibility / printing in black and white
  • Fewer colours, more light/dark shades
  • From darker colours to lighter colours
  • Apply semantics / consistency for clearer storytelling

#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

Over to you!

  • Check the orientation of your plot
  • Choose a colour scheme
  • Apply it to your plot using a named list

See you in 15 minutes! 📊 🎨 ☕

15:00

#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}

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"))

#3 - Add colour to text

Say hello to 📦 {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 = ggtext::element_markdown())

#3 - Add colour to text

When using colour, make sure the text is still readable; use “bold” if needed

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 = ggtext::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 - Starting from the main banana colour, we go darker for dark text - We then go lighter from there to create a light text colour

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

Make sure they don’t appear in the 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 <span style=\"color:",
    banana_colours$Gentoo, "\">**over-ripe**</span> bananas")) +
  theme(plot.title = ggtext::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 = "Cabin",
                            colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown())

#4 - Add text hierarchy

Override it in the title and 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 = "Cabin",
                            colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(colour = banana_colours$dark_text))

#4 - Add text hierarchy

Shift to ggsave() to avoid disappointment when rendering output!

Text size changes between the Viewer and the exported plot, dependent on plot size and resolution. Set these before you spend too much time fine-tuning your text!

ggsave(filename = "penguin-bakeoff.png", 
       width = 8, height = 7.5, dpi = 400, bg = "#FFFFFF")

#4 - Add text hierarchy

Add a line<br>eak where needed!

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 = "Cabin",
                            colour = banana_colours$light_text),
        plot.title = ggtext::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 = "Cabin",
                            colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(
          size = 18,
          family = "Poppins",
          colour = banana_colours$dark_text,
          face = "bold"),
        axis.text = element_text(size = 6,
                                 colour = banana_colours$light_text),
        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” + 🤞


See reference-scripts/02_setting-up-fonts.R to get you started!

#4 - Add text hierarchy

Choosing fonts can be tricky!

  • Brand guidelines
  • Datawrapper guidance - avoid fonts that are too wide/narrow!
  • Websites + inspector tool
  • Oliver Schöndorfer’s exploration of the Font Matrix

#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

Over to you!

  • Think about the main message of your plot
  • Choose a font (or two); use systemfonts::system_fonts() to see what’s installed already
  • Add some text-hierarchy colours to your colour scheme
  • Apply the colours to the different theme() elements

See you in 15 minutes! 🎨 🖋️ ☕

15:00

Part 3 - Annotations

#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 = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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")) +
  ggtext::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 = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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")) +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0(
                         "**Team ", species, "**",
                         "<br><span style = \"color:",
                         banana_colours$light_text,
                         "\">", commentary, "</span>")),
                       family = "Cabin",
                       size = 3.5,
                       width = unit(9, "line"),
                       alpha = 0.9,
                       box.colour = NA) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0(
                         "**Team ", species, "**",
                         "<br><span style = \"color:",
                         banana_colours$light_text,
                         "\">", commentary, "</span>")),
                       family = "Cabin",
                       size = 3.5,
                       width = unit(9, "line"),
                       alpha = 0.9,
                       box.colour = NA) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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

To find the coordinates of the points you want to highlight, try plotly::ggplotly()!

#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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
                       family = "Cabin", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
                       family = "Cabin", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  ggtext::geom_textbox(data = penguin_highlights,
                       aes(label = commentary,
                           x = label_x,
                           y = label_y,
                           # alignment of the box with the box coordinate
                           hjust = left_to_right),
                       family = "Cabin", 
                       size = 3,
                       fill = NA,
                       box.colour = NA) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
                       family = "Cabin", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  ggtext::geom_textbox(data = penguin_highlights,
                       aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right),
                       family = "Cabin", 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 = 0.15,
             alpha = 0.5) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
                       family = "Cabin", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  ggtext::geom_textbox(data = penguin_highlights,
                       aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right,
                           halign = left_to_right),
                       family = "Cabin", 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 = 0.15,
             alpha = 0.5) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
                       family = "Cabin", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  ggtext::geom_textbox(data = penguin_highlights,
                       aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right, halign = left_to_right),
                       family = "Cabin", 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 = 0.15, alpha = 0.5) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
                       family = "Cabin", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  ggtext::geom_textbox(data = penguin_highlights,
                       aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right, halign = left_to_right),
                       family = "Cabin", 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 = 0.15, alpha = 0.5) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text,
                            lineheight = 1.2),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
                       family = "Cabin", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  ggtext::geom_textbox(data = penguin_highlights,
                       aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right, halign = left_to_right),
                       family = "Cabin", 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 = 0.15, alpha = 0.5) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text,
                            lineheight = 1.2),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        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.") +
  ggtext::geom_textbox(data = penguin_summaries,
                       aes(label = paste0("**Team ", species, "**", "<br><span style = \"color:", banana_colours$light_text, "\">", commentary, "</span>")),
                       family = "Cabin", size = 3.5, width = unit(9, "line"), alpha = 0.9, box.colour = NA) +
  ggtext::geom_textbox(data = penguin_highlights,
                       aes(label = commentary, x = label_x, y = label_y, hjust = left_to_right, halign = left_to_right),
                       family = "Cabin", 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 = 0.15, alpha = 0.5) +
  scale_x_continuous(expand = expansion(mult = c(0.2, 0.02))) +
  theme(text = element_text(family = "Cabin", colour = banana_colours$light_text,
                            lineheight = 1.2),
        plot.title = ggtext::element_markdown(size = 18, family = "Poppins", colour = banana_colours$dark_text, face = "bold"),
        axis.text = element_text(size = 6, colour = banana_colours$light_text),
        plot.caption = element_text(size = 6),
        panel.grid = element_line(colour = "#F6F6F5"),
        legend.position = "none")

#7 - Give everything some space to breathe

Seven tips to level up your plots

#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

Over to you!

  • Create some annotations (as many or as few as you like!)
  • Highlight some key points
  • Clarify the story by explaining something in more detail
  • Make use of the colours within the main aes() call if possible

See you in 15 minutes! 🌟 ✨ ☕

15:00

Seven tips to level up your plots

#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 to level up your plots

Seven tips to level up your plots

Seven tips to level up your plots

Bonus track

Sources of inspiration

  • #TidyTuesday on Twitter - get involved!
  • Dataviz / design books
  • Kids books (palettes!)
  • Datajournalists (John Burn-Murdoch - @jburnmurdoch)
  • Artists (local art gallery / Twitter accounts e.g. @womensart1)

Q&A + Plot feedback