From alright to
all ready to
publish



RGlasgow | 23rd May 2024
Advanced Research Center (ARC), Glasgow

Hello đź‘‹

đź‘© Cara Thompson

👩‍💻 Love for patterns in music & language, and a fascination with the human brain %>%

       Psychology PhD %>%

       Analysis of postgraduate medical examinations %>%

       Data Visualisation Consultant


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

The main aim for today

  • Create a basic ggplot
  • See how we can make it better
    • Colour
    • Text
    • Theme
    • Annotations
    • Interactivity

The main aim for today

  • Data from Scottish Environment Protection Agency
    • https://www2.sepa.org.uk/rainfall/
  • {ggplot2}
  • Namespacing
    • ❌ library(dplyr)
    • ✔️ dplyr::mutate

Let’s get some data!

Let’s get some data

rain_data <- read.csv("https://www2.sepa.org.uk/rainfall/api/Month/15201?csv=true",
         header = T) |>
  dplyr::mutate(city = "Edinburgh") |>
  dplyr::bind_rows(read.csv("https://www2.sepa.org.uk/rainfall/api/Month/327234?csv=true",
                     header = T) |>
                     dplyr::mutate(city = "Glasgow")) |>
  # May data is incomplete
  dplyr::filter(Timestamp != "May 2024") |>
  tidyr::separate(Timestamp, into = c("month", "year"), remove = F) |>
  # Make the years back into numbers 
  dplyr::mutate(year = as.numeric(as.character(year))) |>
  # Get month factor levels in right order
  dplyr::mutate(month = factor(month, levels = month.abb))

Let’s get some data

month.abb
 [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
rain_data |> head(10)
    X Timestamp month year Value      city
1   1  Jun 2014   Jun 2014  67.4 Edinburgh
2   2  Jul 2014   Jul 2014  35.2 Edinburgh
3   3  Aug 2014   Aug 2014  90.0 Edinburgh
4   4  Sep 2014   Sep 2014  20.6 Edinburgh
5   5  Oct 2014   Oct 2014  92.4 Edinburgh
6   6  Nov 2014   Nov 2014   9.8 Edinburgh
7   7  Dec 2014   Dec 2014  51.4 Edinburgh
8   8  Jan 2015   Jan 2015  50.2 Edinburgh
9   9  Feb 2015   Feb 2015  27.2 Edinburgh
10 10  Mar 2015   Mar 2015  51.2 Edinburgh

Now for a basic plot…

Now for a basic plot

library(ggplot2)

rain_data |>
  dplyr::group_by(month, city) |>
  dplyr::summarise(mean_rainfall = mean(Value)) |>
  ggplot() +
  geom_bar(aes(x = month, y = mean_rainfall, fill = city),
           stat = "identity")

Now for a basic plot

library(ggplot2)

rain_data |>
  dplyr::group_by(month, city) |>
  dplyr::summarise(mean_rainfall = mean(Value)) |>
  ggplot() +
  geom_bar(aes(x = month, y = mean_rainfall, fill = city),
           stat = "identity") +
  theme_minimal()

Now for a basic plot

library(ggplot2)

rain_data |>
  dplyr::group_by(month, city) |>
  dplyr::summarise(mean_rainfall = mean(Value)) |>
  ggplot() +
  geom_bar(aes(x = month, y = mean_rainfall, fill = city),
           stat = "identity",
           position = "dodge") +
  theme_minimal()

Now for a basic plot

“Wait a minute…” | Check it’s the right type of plot!

rain_data |>
  ggplot() +
  geom_point(aes(x = month, y = Value, colour = city)) +
  theme_minimal()

Now for a basic plot

Make it easier to distinguish between the cities

rain_data |>
  ggplot() +
  geom_point(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                      TRUE ~ as.numeric(month) + 0.1),
                 y = Value,
                 colour = city)) +
  theme_minimal()

Now for a basic plot

Make it the grid more useful

rain_data |>
  ggplot() +
  geom_point(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                      TRUE ~ as.numeric(month) + 0.1),
                 y = Value,
                 colour = city)) +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL) +
  theme_minimal()

Now for a basic plot

Fix the labels

rain_data |>
  ggplot() +
  geom_point(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                      TRUE ~ as.numeric(month) + 0.1),
                 y = Value,
                 colour = city)) +
  theme_minimal() +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb)

Now for a basic plot

Fix the labels - a safer way!

rain_data |>
  ggplot() +
  geom_point(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                      TRUE ~ as.numeric(month) + 0.1),
                 y = Value,
                 colour = city),
             alpha = 0.8) +
  theme_minimal() +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12])

Now for a basic plot

See if you can remove any text

rain_data |>
  ggplot() +
  geom_point(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                      TRUE ~ as.numeric(month) + 0.1),
                 y = Value,
                 colour = city)) +
  theme_minimal() +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  theme(axis.title = element_blank())

Now for a basic plot

See if you can remove any text

rain_data |>
  ggplot() +
  geom_point(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                      TRUE ~ as.numeric(month) + 0.1),
                 y = Value,
                 colour = city)) +
  theme_minimal() +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  theme(axis.title = element_blank(),
        legend.title = element_blank())

Now for a basic plot

We probably need to give them some context

rain_data |>
  ggplot() +
  geom_point(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                      TRUE ~ as.numeric(month) + 0.1),
                 y = Value,
                 colour = city)) +
  labs(title = "Rainfall in Glasgow and Edinburgh over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024.") +
  theme_minimal() +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  theme(axis.title = element_blank(),
        legend.title = element_blank())

Now for a basic plot

Make the y axis clearer

rain_data |>
  ggplot() +
  geom_point(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                      TRUE ~ as.numeric(month) + 0.1),
                 y = Value,
                 colour = city)) +
  labs(title = "Rainfall in Glasgow and Edinburgh over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024.") +
  theme_minimal() +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_y_continuous(labels = function(x) paste(x, "mm")) +
  theme(axis.title = element_blank(),
        legend.title = element_blank())

#1 Use meaningful colours

#1 Use meaningful colours

  • Make it easier to remember what’s what
  • Blend in some brand colours
  • Check for accessibility
  • Implement!

#1 Use meaningful colours

Glasgow and Edinburgh

rain_data |>
  dplyr::group_by(month, city) |>
  dplyr::summarise(mean_rainfall = mean(Value)) |>
  ggplot() +
  geom_bar(aes(x = month, y = mean_rainfall, fill = city),
           position = "dodge",
           stat = "identity") +
  theme_minimal()

#1 Use meaningful colours

Glasgow and Edinburgh

rain_data |>
  dplyr::group_by(month, city) |>
  dplyr::summarise(mean_rainfall = mean(Value)) |>
  ggplot() +
  geom_bar(aes(x = month, y = mean_rainfall, fill = city),
           position = "dodge",
           stat = "identity") +
  scale_fill_manual(values = c("#7691b1",
                               "#4f3c78")) +
  theme_minimal()

#1 Use meaningful colours

Glasgow and Edinburgh

rain_data |>
  dplyr::group_by(month, city) |>
  dplyr::summarise(mean_rainfall = mean(Value)) |>
  dplyr::mutate(city = factor(city, levels = c("Glasgow", "Edinburgh"))) |>
  ggplot() +
  geom_bar(aes(x = month, y = mean_rainfall, fill = city),
           position = "dodge",
           stat = "identity") +
  scale_fill_manual(values = c("#7691b1",
                               "#4f3c78")) +
  theme_minimal()

#1 Use meaningful colours

Glasgow and Edinburgh

rain_data |>
  dplyr::group_by(month, city) |>
  dplyr::summarise(mean_rainfall = mean(Value)) |>
  dplyr::mutate(city = factor(city, levels = c("Glasgow", "Edinburgh"))) |>
  ggplot() +
  geom_bar(aes(x = month, y = mean_rainfall, fill = city),
           position = "dodge",
           stat = "identity") +
  scale_fill_manual(values = c("Edinburgh" = "#7691b1",
                               "Glasgow" = "#4f3c78")) +
  theme_minimal()

#1 Use meaningful colours

Glasgow and Edinburgh

rain_data |>
  ggplot() +
  geom_jitter(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                       TRUE ~ as.numeric(month) + 0.1),
                  y = Value,
                  colour = city),
              size = 5,
              width = 0.05,
              height = 0) +
  labs(title = "Rainfall in Glasgow and Edinburgh over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024.") +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none")

#1 Use meaningful colours

Glasgow and Edinburgh over the years

rain_data |>
  ggplot() +
  geom_jitter(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                       TRUE ~ as.numeric(month) + 0.1),
                  y = Value,
                  colour = city,
                  alpha = year),
              size = 5,
              width = 0.05,
              height = 0) +
  labs(title = "Rainfall in Glasgow and Edinburgh over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024.") +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none")

#1 Use meaningful colours

Glasgow and Edinburgh over the years

rain_data |>
  ggplot() +
  geom_jitter(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                       TRUE ~ as.numeric(month) + 0.1),
                  y = Value,
                  colour = city,
                  alpha = year),
              size = 5,
              width = 0.05,
              height = 0) +
  labs(title = "Rainfall in Glasgow and Edinburgh over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024. The more faded the dot, the further ago the year it represents.") +
  scale_alpha(range = c(0.3, 1)) +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none")

#2 Optimise text

#2 Optimise text

Starting point

basic_plot <- rain_data |>
  ggplot() +
  geom_jitter(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                       TRUE ~ as.numeric(month) + 0.1),
                  y = Value,
                  colour = city,
                  alpha = year),
              size = 5,
              width = 0.05,
              height = 0) +
  labs(title = "Rainfall in Glasgow and Edinburgh over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024. The more faded the dot, the further ago the year it represents.") +
  scale_alpha(range = c(0.3, 1)) +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none")

basic_plot

#2 Optimise text

Starting point

basic_plot +
  theme_minimal(base_size = 20) +
  theme(axis.title = element_blank(),
        legend.position = "none")

#2 Optimise text

Make the text fit - the easy way!

basic_plot +
  theme_minimal(base_size = 20) +
  theme(axis.title = element_blank(),
        legend.position = "none",
        plot.subtitle = ggtext::element_textbox_simple())

#2 Optimise text

Let’s add some personality

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.subtitle = ggtext::element_textbox_simple())

#2 Optimise text

Let’s add some personality

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro"),
        plot.subtitle = ggtext::element_textbox_simple())

#2 Optimise text

Let’s add some hierarchy

#2 Optimise text

Let’s add some hierarchy

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro"),
        plot.subtitle = ggtext::element_textbox_simple())

#2 Optimise text

Let’s add some hierarchy

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  size = 32),
        plot.subtitle = ggtext::element_textbox_simple())

#2 Optimise text

Let’s add some hierarchy

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple())

#2 Optimise text

Let’s add some hierarchy

basic_plot +
  theme_minimal(base_size = 16) +
  theme(text = element_text(family = "Nunito Sans"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple())

#2 Optimise text

Let’s add some hierarchy

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  face = "bold",
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple())

#2 Optimise text

Let’s add some hierarchy

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans",
                            colour = "#2B2529"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  face = "bold",
                                  colour = "#10090E",
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple())

#3 Make it your own

#3 Make it your own

Starting point

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans",
                            colour = "#2B2529"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  face = "bold",
                                  colour = "#10090E",
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple())

#3 Make it your own

Nicer grid colour

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans",
                            colour = "#2B2529"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  face = "bold",
                                  colour = "#10090E",
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple(),
        panel.grid = element_line(colour = "#F1F1F2"))

#3 Make it your own

Add a background colour?

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans",
                            colour = "#2B2529"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  face = "bold",
                                  colour = "#10090E",
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple(),
        panel.grid = element_line(colour = "#F1F1F2"),
        plot.background = element_rect(colour = "#FEFCF7",
                                       fill = "#FEFCF7"))

#3 Make it your own

Give everything some space to breathe

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans",
                            colour = "#2B2529"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  face = "bold",
                                  colour = "#10090E",
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple(
          size = ggplot2::rel(1.2),
          lineheight = 1.3,
          # Acronym: TRouBLe (Top, right, bottom, left)
          margin = ggplot2::margin(10, 0, 20, 0,
                                   unit = "points")),
        panel.grid = element_line(colour = "#F1F1F2"),
        plot.background = element_rect(colour = "#FEFCF7",
                                       fill = "#FEFCF7"),
        plot.margin = ggplot2::margin(20, 30, 20, 30,
                                      unit = "points"))

#3 Make it your own

Declutter, declutter, declutter

basic_plot +
  theme_minimal(base_size = 20) +
  theme(text = element_text(family = "Nunito Sans",
                            colour = "#2B2529"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  face = "bold",
                                  colour = "#10090E",
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple(
          size = ggplot2::rel(1.2),
          lineheight = 1.3,
          margin = ggplot2::margin(10, 0, 20, 0,
                                   unit = "points")),
        panel.grid = element_line(colour = "#F1F1F2"),
        panel.grid.major.x = element_blank(),
        plot.background = element_rect(colour = "#FEFCF7",
                                       fill = "#FEFCF7"),
        plot.margin = ggplot2::margin(20, 30, 20, 30,
                                      unit = "points"))

Shameless plug alert!

Shameless plug alert!

Bespoke dataviz design system packages

basic_plot +
  theme(legend.position = "none",
        panel.grid.major.x = element_blank(),
        axis.title = element_blank())

Shameless plug alert!

Bespoke dataviz design system packages

basic_plot +
  ophelia::theme_ophelia(base_text_size = 16) +
  theme(legend.position = "none",
        panel.grid.major.x = element_blank(),
        axis.title = element_blank())

Shameless plug alert!

Bespoke dataviz design system packages

basic_plot +
  ophelia::theme_ophelia(base_text_size = 16,
                         background_colour = FALSE) +
  ophelia::scale_colour_ophelia("cool_colours") +
  theme(legend.position = "none",
        panel.grid.major.x = element_blank(),
        axis.title = element_blank())

Shameless plug alert!

Bespoke dataviz design system packages

basic_plot +
  ophelia::theme_ophelia(base_text_size = 16,
                         background_colour = FALSE) +
  ophelia::scale_colour_ophelia("warm_colours") +
  theme(legend.position = "none",
        panel.grid.major.x = element_blank(),
        axis.title = element_blank())

#4 Add annotations for context

#4 Add annotations for context

Starting point

themed_plot <- basic_plot +
  theme_minimal(base_size = 16) +
  theme(text = element_text(family = "Nunito Sans",
                            colour = "#2B2529"),
        axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(family = "Crimson Pro", 
                                  face = "bold",
                                  colour = "#10090E",
                                  size = rel(1.6)),
        plot.subtitle = ggtext::element_textbox_simple(
          size = ggplot2::rel(1.2),
          lineheight = 1.3,
          margin = ggplot2::margin(10, 0, 20, 0,
                                   unit = "points")),
        panel.grid = element_line(colour = "#F1F1F2"),
        panel.grid.major.x = element_blank(),
        plot.background = element_rect(colour = "#FEFCF7",
                                       fill = "#FEFCF7"),
        plot.margin = ggplot2::margin(20, 30, 20, 30,
                                      unit = "points"))

themed_plot

#4 Add annotations for context

Add the mean monthly rainfall

themed_plot +
  geom_hline(aes(yintercept = mean(Value)))

#4 Add annotations for context

Hello {geomtextpath}!

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean monthly rainfall: ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7")

#4 Add annotations for context

Style the label

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean monthly rainfall: ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E")

#4 Add annotations for context

Adjust the position of the label

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean monthly rainfall: ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9)

#4 Add annotations for context

Add the UK mean for comparison…

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean (Edinburgh & Glasgow): ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9) +
  geomtextpath::geom_labelhline(aes(yintercept = 107.5,
                                    label = paste("Mean monthly UK rainfall: ", round(107.5), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.8,
                                linetype = 2,
                                colour = "#2B2529",
                                hjust = 0.9)

#4 Add annotations for context

Highlight interesting observations

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean (Edinburgh & Glasgow): ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9) +
  geomtextpath::geom_labelhline(aes(yintercept = 107.5,
                                    label = paste("Mean monthly UK rainfall: ", round(107.5), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.8,
                                linetype = 2,
                                colour = "#2B2529",
                                hjust = 0.9) +
  ggtext::geom_textbox(data = dplyr::filter(rain_data, Value == max(Value)),
                       aes(x = as.numeric(month), y = Value, label = "Here's some text!"))

#4 Add annotations for context

Highlight interesting observations

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean (Edinburgh & Glasgow): ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9) +
  geomtextpath::geom_labelhline(aes(yintercept = 107.5,
                                    label = paste("Mean monthly UK rainfall: ", round(107.5), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.8,
                                linetype = 2,
                                colour = "#2B2529",
                                hjust = 0.9) +
  ggtext::geom_textbox(data = dplyr::filter(rain_data, Value == max(Value)),
                       aes(x = as.numeric(month), y = Value, label = "Here's some text!"),
                       hjust = 0, halign = 0)

#4 Add annotations for context

Highlight interesting observations

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean (Edinburgh & Glasgow): ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9) +
  geomtextpath::geom_labelhline(aes(yintercept = 107.5,
                                    label = paste("Mean monthly UK rainfall: ", round(107.5), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.8,
                                linetype = 2,
                                colour = "#2B2529",
                                hjust = 0.9) +
  ggtext::geom_textbox(data = dplyr::filter(rain_data, Value == max(Value)),
                       aes(x = as.numeric(month) + 0.3, y = Value - 20, label = "Here's some text!"),
                       hjust = 0, halign = 0)

#4 Add annotations for context

Highlight interesting observations

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean (Edinburgh & Glasgow): ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9) +
  geomtextpath::geom_labelhline(aes(yintercept = 107.5,
                                    label = paste("Mean monthly UK rainfall: ", round(107.5), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.8,
                                linetype = 2,
                                colour = "#2B2529",
                                hjust = 0.9) +
  ggtext::geom_textbox(data = dplyr::filter(rain_data, Value == max(Value)),
                       aes(x = as.numeric(month) + 0.3, y = Value - 20, label = "Here's some text!"),
                       hjust = 0, halign = 0,
                       fill = NA, box.colour = NA,
                       colour = "#10090E",
                       family = "Nunito Sans")

#4 Add annotations for context

Highlight interesting observations

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean (Edinburgh & Glasgow): ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9) +
  geomtextpath::geom_labelhline(aes(yintercept = 107.5,
                                    label = paste("Mean monthly UK rainfall: ", round(107.5), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.8,
                                linetype = 2,
                                colour = "#2B2529",
                                hjust = 0.9) +
  ggtext::geom_textbox(data = dplyr::filter(rain_data, Value == max(Value)),
                       aes(x = as.numeric(month) + 0.3, y = Value - 20, 
                           label = paste0("The wettest month in the dataset was recorded in ",
                                          month, " in ", city, " with a total rainfall of ",
                                          Value, " mm.")),
                       hjust = 0, halign = 0,
                       fill = NA, box.colour = NA,
                       colour = "#10090E",
                       family = "Nunito Sans")

#4 Add annotations for context

Highlight interesting observations

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean (Edinburgh & Glasgow): ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9) +
  geomtextpath::geom_labelhline(aes(yintercept = 107.5,
                                    label = paste("Mean monthly UK rainfall: ", round(107.5), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.8,
                                linetype = 2,
                                colour = "#2B2529",
                                hjust = 0.9) +
  ggtext::geom_textbox(data = dplyr::filter(rain_data, Value == max(Value)),
                       aes(x = as.numeric(month) + 0.3, y = Value - 20, 
                           label = paste0("The wettest month in the dataset was recorded in ",
                                          month, " in ", city, " with a total rainfall of ",
                                          Value, " mm.")),
                       hjust = 0, halign = 0,
                       fill = NA, box.colour = NA,
                       colour = "#10090E",
                       family = "Nunito Sans") +
  geom_curve(data = dplyr::filter(rain_data, Value == max(Value)),
             aes(x = as.numeric(month) + 0.3, y = Value - 20, 
             xend = as.numeric(month), yend = Value - 10),
             arrow = arrow(length = unit(5, "pt")),
             curvature = -0.2)

#4 Add annotations for context

Place legend within the title

themed_plot +
  geomtextpath::geom_labelhline(aes(yintercept = mean(Value),
                                    label = paste("Mean (Edinburgh & Glasgow): ", round(mean(Value)), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.9,
                                colour = "#10090E",
                                hjust = 0.9) +
  geomtextpath::geom_labelhline(aes(yintercept = 107.5,
                                    label = paste("Mean monthly UK rainfall: ", round(107.5), "mm")),
                                fill = "#FEFCF7",
                                family = "Nunito Sans",
                                alpha = 0.8,
                                linetype = 2,
                                colour = "#2B2529",
                                hjust = 0.9) +
  ggtext::geom_textbox(data = dplyr::filter(rain_data, Value == max(Value)),
                       aes(x = as.numeric(month) + 0.3, y = Value - 20, 
                           label = paste0("The wettest month in the dataset was recorded in ",
                                          month, " in ", city, " with a total rainfall of ",
                                          Value, " mm.")),
                       hjust = 0, halign = 0,
                       fill = NA, box.colour = NA,
                       colour = "#10090E",
                       family = "Nunito Sans") +
  labs(title = "Rainfall in <span style='color:#4f3c78'>Glasgow</span> and <span style='color:#7691b1'>Edinburgh</span> over the years") +
  geom_curve(data = dplyr::filter(rain_data, Value == max(Value)),
             aes(x = as.numeric(month) + 0.3, y = Value - 20, 
                 xend = as.numeric(month), yend = Value - 10),
             arrow = arrow(length = unit(5, "pt")),
             curvature = -0.2) +
  theme(plot.title = ggtext::element_markdown(family = "Crimson Pro", 
                                      face = "bold",
                                      colour = "#10090E",
                                      size = rel(1.6)))

#5 Go interactive!

#5 Go interactive!

rain_data |>
  ggplot() +
  geom_jitter(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                       TRUE ~ as.numeric(month) + 0.1),
                  y = Value,
                  colour = city,
                  alpha = year),
              size = 5,
              width = 0.05,
              height = 0) +
labs(title = "Rainfall in <span style='color:#4f3c78'>Glasgow</span> and <span style='color:#7691b1'>Edinburgh</span> over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024. The more faded the dot, the further ago the year it represents.") +
  scale_alpha(range = c(0.3, 1)) +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none") +
  ophelia::theme_ophelia() +
  theme(legend.position = "none",
        axis.title = element_blank(),
        plot.title = ggtext::element_markdown(family = "Crimson Pro", 
                                      face = "bold",
                                      colour = "#10090E",
                                      size = rel(1.6)))

#5 Go interactive!

Hello, {ggiraph}!

interactive_version <- rain_data |>
  ggplot() +
  ggiraph::geom_jitter_interactive(aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                                       TRUE ~ as.numeric(month) + 0.1),
                  y = Value,
                  colour = city,
                  alpha = year),
              size = 5,
              width = 0.05,
              height = 0) +
  labs(title = "Rainfall in <span style='color:#4f3c78'>Glasgow</span> and <span style='color:#7691b1'>Edinburgh</span> over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024. The more faded the dot, the further ago the year it represents.") +
  scale_alpha(range = c(0.3, 1)) +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none") +
  ophelia::theme_ophelia() +
  theme(legend.position = "none",
        axis.title = element_blank(),
        plot.title = ggtext::element_markdown(family = "Crimson Pro", 
                                      face = "bold",
                                      colour = "#10090E",
                                      size = rel(1.6)))

ggiraph::girafe(ggobj = interactive_version)

#5 Go interactive!

interactive_version <- rain_data |>
  ggplot() +
  ggiraph::geom_jitter_interactive(
    aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                             TRUE ~ as.numeric(month) + 0.1),
        y = Value,
        colour = city,
        alpha = year,
        tooltip = paste0(city, ", ", Timestamp, "<br><b>", Value, "mm</b>")),
    size = 5,
    width = 0.05,
    height = 0) +
  labs(title = "Rainfall in <span style='color:#4f3c78'>Glasgow</span> and <span style='color:#7691b1'>Edinburgh</span> over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024. The more faded the dot, the further ago the year it represents.") +
  scale_alpha(range = c(0.3, 1)) +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none") +
  ophelia::theme_ophelia() +
  theme(legend.position = "none",
        axis.title = element_blank(),
        plot.title = ggtext::element_markdown(family = "Crimson Pro", 
                                      face = "bold",
                                      colour = "#10090E",
                                      size = rel(1.6)))

ggiraph::girafe(ggobj = interactive_version)

#5 Go interactive!

Style the tooltip…

interactive_version <- rain_data |>
  ggplot() +
  ggiraph::geom_jitter_interactive(
    aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                             TRUE ~ as.numeric(month) + 0.1),
        y = Value,
        colour = city,
        alpha = year,
        tooltip = paste0(city, ", ", Timestamp, "<br><b>", Value, "mm</b>")),
    size = 5,
    width = 0.05,
    height = 0) +
 labs(title = "Rainfall in <span style='color:#4f3c78'>Glasgow</span> and <span style='color:#7691b1'>Edinburgh</span> over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024. The more faded the dot, the further ago the year it represents.") +
  scale_alpha(range = c(0.3, 1)) +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none") +
  ophelia::theme_ophelia() +
  theme(legend.position = "none",
        axis.title = element_blank(),
        plot.title = ggtext::element_markdown(family = "Crimson Pro", 
                                      face = "bold",
                                      colour = "#10090E",
                                      size = rel(1.6)))

ggiraph::girafe(ggobj = interactive_version,
                option = list(ggiraph::opts_tooltip(
                    css = "background-color:#2B2529;color:#FEFCF7;padding:7.5px;letter-spacing:0.025em;line-height:1.3;border-radius:5px;font-family:Nunito Sans;")))

#5 Go interactive!

… the easier way!

interactive_version <- rain_data |>
  ggplot() +
  ggiraph::geom_jitter_interactive(
    aes(x = dplyr::case_when(city == "Glasgow" ~ as.numeric(month) - 0.1,
                             TRUE ~ as.numeric(month) + 0.1),
        y = Value,
        colour = city,
        alpha = year,
        tooltip = paste0(city, ", ", Timestamp, "<br><b>", Value, "mm</b>")),
    size = 5,
    width = 0.05,
    height = 0) +
  labs(title = "Rainfall in <span style='color:#4f3c78'>Glasgow</span> and <span style='color:#7691b1'>Edinburgh</span> over the years",
       subtitle = "Each dot represents the total rainfall within a given month from 2014 to 2024. The more faded the dot, the further ago the year it represents.") +
  scale_alpha(range = c(0.3, 1)) +
  scale_x_continuous(breaks = c(1:12), minor_breaks = NULL,
                     labels = month.abb[1:12]) +
  scale_colour_manual(values = c("Glasgow" = "#4f3c78",
                                 "Edinburgh" = "#7691b1")) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none") +
  ophelia::theme_ophelia() +
  theme(legend.position = "none",
        axis.title = element_blank(),
        plot.title = ggtext::element_markdown(family = "Crimson Pro", 
                                      face = "bold",
                                      colour = "#10090E",
                                      size = rel(1.6)))

ggiraph::girafe(ggobj = interactive_version,
                option = list(ggiraph::opts_tooltip(
                    css = ophelia::tooltip_css)))

From alright to all ready to publish

  • Colours
  • Text
  • Theme
  • Annotations
  • Interactivity
Over to you!
hello@cararthompson.com