“So much more than pretty graphs”

Recent adventures in automating dataviz solutions
Cara Thompson | Building Stories with Data

The Data Lab Community | Women in Data & Ai Meetup
29th Feb 2024

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

Hello 👋

👩 Cara Thompson

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

       Psychology PhD %>%

       Analysis of postgraduate medical examinations %>%

            if (year %in% c(2019, 2021)) {

             family <- family + 👶

             } %>%

       Data Visualisation Consultant

💙 Helping others maximise the impact of their expertise

Ok, but how?


Dataviz Training & Mentoring

with R or tool-agnostic


Dataviz Design Systems

implemented in R otherwise


Data-to-viz Commissions

incl. “parameterised” plots

Data-to-viz

Straight from the data to conversation-starting plots

Data-to-viz

Data-to-viz

Data-to-viz

Data-to-viz

I ❤️ the Great British Bake Off - {bakeoff}

library(tidyverse)

all_the_bakes <- bakeoff::challenges %>% 
  select(-technical) %>%
  pivot_longer(c(signature, showstopper)) %>%
  pull(value) %>%
  tolower()

key_components <- tibble(
  component = c("Chocolate",
                "Raspberry",
                "Génoise or Sponge",
                "Rhubarb"),
  count = c(sum(grepl("chocolat", all_the_bakes)),
            sum(grepl("raspberr", all_the_bakes)),
            sum(grepl("sponge|genoise|génoise", all_the_bakes)),
            sum(grepl("rhubarb", all_the_bakes)))
)

key_components
# A tibble: 4 x 2
  component         count
  <chr>             <int>
1 Chocolate           206
2 Raspberry            76
3 Génoise or Sponge    12
4 Rhubarb              30

Data-to-viz

I ❤️ the Great British Bake Off - {bakeoff}

key_components %>%
  arrange(count) %>%
  mutate(component = factor(component, 
                            levels = component)) %>%
  ggplot(aes(x = component,
             y = count,
             fill = component,
             colour = component)) +
  geom_bar(stat = "identity") +
  theme_minimal(base_size = 18) +
  coord_flip()

Data-to-viz

Let’s add some annotations and make it easier to read…

key_components %>%
  arrange(count) %>%
  mutate(component = factor(component, 
                            levels = component)) %>%
  ggplot(aes(x = component,
             y = count,
             fill = component,
             colour = "white")) +
  geom_bar(stat = "identity") +
  labs(title = "Everybody loves chocolate",
       subtitle = "Out of our four key cake components, selected arbitrarily for the purpose of this demonstration, chocolate stood out as the most frequently used by far. No huge surprises there!",
       y = "Number of bakes") +
  theme_minimal(base_size = 18) +
  coord_flip() +
  theme(text = element_text(size = 12,
                            colour = "#4C3232"),
        legend.position = "none",
        axis.title.y = element_blank(),
        plot.subtitle = ggtext::element_textbox_simple(size = 16,
                                                       vjust = 1,
                                                       margin = margin(0, 0, 12, 0)),
        plot.title = element_text(size = 24,
                                  face = "bold",
                                  colour = "#200000",
                                  margin = margin(12, 0, 12, 0)),
        axis.text = element_text(colour = "#4C3232")) +
  scale_y_continuous(expand = expansion(c(0, 0.02))) +
  ggtext::geom_textbox(aes(label = paste0("<span style='font-size:32pt'><br>",
                                          count,
                                          "<br></span>",
                                          component, " bakes"),
                           hjust =  case_when(count < max(count)/2 ~ 0,
                                              TRUE ~ 1),
                           halign = case_when(count < max(count)/2 ~ 0,
                                              TRUE ~ 1),
                           colour = case_when(count < max(count)/2 ~ "#4C3232",
                                              TRUE ~ "white")),
                       vjust = 0.45,
                       size = 7,
                       fill = NA,
                       box.colour = NA,
                       fontface = "bold") +
  scale_colour_identity() +
  theme(axis.text.y = element_blank(),
        plot.title.position = "plot",
        axis.text.x = element_blank(),
        axis.title.x = element_blank(),
        panel.grid = element_blank())

Data-to-viz

Let’s add some more meaningful colours, and a bit of personality…

component_colours <- c("Chocolate" = "#300300",
                       "Raspberry" = "#a8001a",
                       "Génoise or Sponge" = "#d4943b",
                       "Rhubarb" = "#df7864")

key_components %>%
  arrange(count) %>%
  mutate(component = factor(component, 
                            levels = component)) %>%
  ggplot(aes(x = component,
             y = count,
             fill = component,
             colour = component)) +
  geom_bar(stat = "identity",
           colour = "white") +
  labs(title = "Everybody loves chocolate",
       subtitle = "Out of our four key cake components, selected arbitrarily for the purpose of this demonstration, chocolate stood out as the most frequently used by far. No huge surprises there!",
       y = "Number of bakes") +
  theme_minimal(base_size = 18) +
  coord_flip() +
  scale_fill_manual(values = component_colours) +
  scale_y_continuous(expand = expansion(c(0, 0.02))) +
  ggtext::geom_textbox(aes(label = paste0("<span style='font-size:32pt'><br>",
                                          count,
                                          "<br></span>",
                                          component, " bakes"),
                           hjust =  case_when(count < max(count)/2 ~ 0,
                                              TRUE ~ 1),
                           halign = case_when(count < max(count)/2 ~ 0,
                                              TRUE ~ 1),
                           colour = case_when(count < max(count)/2 ~ "#4C3232",
                                              TRUE ~ "white")),
                       vjust = 0.45,
                       size = 7,
                       fill = NA,
                       family = "Cabin",
                       box.colour = NA,
                       fontface = "bold") +
  scale_colour_identity() +
  theme(text = element_text(family = "Cabin", 
                            size = 12, 
                            colour = "#4C3232"),
        legend.position = "none",
        axis.title.y = element_blank(),
        plot.title = element_text(family = "OPTIAuvantGothic-DemiBold", 
                                  size = 24, 
                                  face = "bold",
                                  colour = "#200000",
                                  margin = margin(12, 0, 12, 0)),
        axis.text = element_text(colour = "#4C3232"),
        axis.text.y = element_blank(),
        plot.title.position = "plot",
        axis.text.x = element_blank(),
        axis.title.x = element_blank(),
        panel.grid = element_blank(),
        plot.margin = margin(rep(18, 4)),
        plot.subtitle = ggtext::element_textbox_simple(
          size = 16,
          vjust = 1,
          margin = margin(0, 0, 12, 0),
          lineheight = 1.3))

Data-to-viz

And now, let’s turn it into a data-to-viz function!

bake_off_plot <- function(plot_data = all_the_bakes) {
  
  tibble(
    component = c("Chocolate",
                  "Raspberry",
                  "Génoise or Sponge",
                  "Rhubarb"),
    count = c(sum(grepl("chocolat", plot_data)),
              sum(grepl("raspberr", plot_data)),
              sum(grepl("sponge|genoise|génoise", plot_data)),
              sum(grepl("rhubarb", plot_data)))
  ) %>%
    arrange(count) %>%
  mutate(component = factor(component, 
                            levels = component)) %>%
  ggplot(aes(x = component,
             y = count,
             fill = component,
             colour = component)) +
  geom_bar(stat = "identity",
           colour = "white") +
  labs(title = "Everybody loves chocolate",
       subtitle = "Out of our four key cake components, selected arbitrarily for the purpose of this demonstration, chocolate stood out as the most frequently used by far. No huge surprises there!",
       y = "Number of bakes") +
  theme_minimal(base_size = 18) +
  coord_flip() +
  scale_fill_manual(values = component_colours) +
  scale_y_continuous(expand = expansion(c(0, 0.02))) +
  ggtext::geom_textbox(aes(label = paste0("<span style='font-size:32pt'><br>",
                                          count,
                                          "<br></span>",
                                          component, " bakes"),
                           hjust =  case_when(count < max(count)/2 ~ 0,
                                              TRUE ~ 1),
                           halign = case_when(count < max(count)/2 ~ 0,
                                              TRUE ~ 1),
                           colour = case_when(count < max(count)/2 ~ "#4C3232",
                                              TRUE ~ "white")),
                       vjust = 0.45,
                       size = 7,
                       fill = NA,
                       family = "Cabin",
                       box.colour = NA,
                       fontface = "bold") +
  scale_colour_identity() +
  theme(text = element_text(family = "Cabin", 
                            size = 12, 
                            colour = "#4C3232"),
        legend.position = "none",
        axis.title.y = element_blank(),
        plot.title = element_text(family = "OPTIAuvantGothic-DemiBold", 
                                  size = 24, 
                                  face = "bold",
                                  colour = "#200000",
                                  margin = margin(12, 0, 12, 0)),
        axis.text = element_text(colour = "#4C3232"),
        axis.text.y = element_blank(),
        plot.title.position = "plot",
        axis.text.x = element_blank(),
        axis.title.x = element_blank(),
        panel.grid = element_blank(),
        plot.margin = margin(rep(18, 4)),
        plot.subtitle = ggtext::element_textbox_simple(
          size = 16,
          vjust = 1,
          margin = margin(0, 0, 12, 0),
          lineheight = 1.3))
}
bake_off_plot()

Wait but why?

Wait but why?

bake_off_plot(all_the_bakes[101:length(all_the_bakes)])

Wait but why?

bake_off_plot(sample(all_the_bakes, 250))

bake_off_plot(all_the_bakes[500:750])

Parameterised plots

What if we exclude specific seasons of the Bake Off…?

parameterised_bake_off_plot <- 
  function(series_to_exclude,
           df = bakeoff::challenges) {
    
    some_of_the_bakes <- df %>% 
      select(-technical) %>%
      pivot_longer(c(signature, showstopper)) %>%
      filter(!series %in% series_to_exclude) %>%
      pull(value) %>%
      tolower()
    
    bake_off_plot(some_of_the_bakes)
    
  }
parameterised_bake_off_plot(
  series_to_exclude = 5)

Parameterised plots

What if we exclude specific seasons of the Bake Off…?

parameterised_bake_off_plot(
  series_to_exclude = c(1, 7))

parameterised_bake_off_plot(
  series_to_exclude = c(3:4))

Parameterised plots

What if we exclude specific seasons of the Bake Off…?

parameterised_bake_off_plot(
  series_to_exclude = c(4, 6))

parameterised_bake_off_plot(
  series_to_exclude = c(1:3))

Parameterised plots

What if we exclude specific seasons of the Bake Off…?

parameterised_bake_off_plot(
  series_to_exclude = c(2))

parameterised_bake_off_plot(
  series_to_exclude = c())

Parameterised plots

Dataviz Design Systems

From same old squirrel to snazzy squirrel

Dataviz Design System

A decision-making shortcut


Dataviz Design System

A decision-making shortcut

  • What kind of colours would you like to be associated with your project?
  • How much personality would you like to convey in the fonts?
  • What is the maximum number of colours you typically need in your visualisations?


Dataviz Design System

A decision-making shortcut

  • Are there any key concepts you report on regularly for which we should create a colour-semantic pairing?
  • What type of visualisations do you use a lot?
  • Are there any brand guidelines we can build on?


Semantics

Semantics

Semantics

Semantics

Semantics

Which one is Kiki?

Semantics

Sound symbolism

Semantics

Which triangle is the happiest?

Brand colours / preferences

“Feminine but not too floral or sickly sweet…”

“I need about 20 colours…”

Semantics + branding

“Negative and positive, but not red and green”

Semantics + style + typography

Semantics + style + typography

Semantics + style + typography

Decision making shortcut

Semantics + style + typography + parameterisation

Quarto / Powerpoint templates

Dataviz Design System

Quarto / Powerpoint templates

Slides!

PowerBI Dashboards

Dataviz Design System

PowerBI Dashboards ❤️ Accidental Elmer

Dashboards!

“But… before the end of the pipeline?”

Yes, before the end of the pipeline!

  • Forces you to think carefully about what data you’ll collect
  • Easily keep an eye on things as they develop
  • Quickly present memorable graphs to internal stakeholders
    • Was that Rhubarb or Génoise at the bottom?
  • Quick turnaround once we have all the data

More than just a time saver!

  • Accessibility baked in from the start
  • Unified aesthetic across internal memos and external publications
  • Brand recognition
  • No more procrastinating picking colours
    • or creating squirrels illustrations…

Bringing it all together: #BarsBeRight

#BarsBeRight

#BarsBeRight

What if…?

  • there was an easier way of figuring this out
  • that anyone could use
  • so that we could check the accuracy of the bar lengths
  • and also see what we might be missing by choosing bars
  • and make use of the meme effect…

Wait a minute… WebR!

Styling

  • The colours you input
df <- dplyr::tibble(labels,
              numbers,
              unit,
              colours)

ggplot2::ggplot(df,
                ggplot2::aes(x = labels,
                             y = numbers,
                             fill = colours),
                colour = "#FFFFFF") +
  (...) + 
  ggplot2::scale_fill_identity() 
  • The theme I created, which is applied to all the plots
theme_barsberight <- function(){
  ggplot2::theme_void() +
    ggplot2::theme(panel.background = ggplot2::element_rect(colour = "#000000", fill = "#FFFFFF95"),
                   plot.margin = ggplot2::unit(c(5, 5, 5, 5), "pt"),
                   # Using web safe fonts
                   plot.caption = ggplot2::element_text(family = "Trebuchet MS"),
                   plot.title = ggplot2::element_text(family = "Impact",
                                                      colour = "#1A242F",
                                                      size = 24,
                                                      hjust = 0.5,
                                                      margin = ggplot2::unit(c(20, 0, 12, 0), "pt")))
}

plot + 
  ggplot2::scale_fill_identity() +
  theme_barsberight()

Parameterising the unknown

With all we’ve talked about, here’s the easy bit!

replace_data <- function() {
  dplyr::tibble(labels,
                numbers,
                unit,
                colours) |>
    # Getting everything to stay in the right order!
    dplyr::mutate(labels = factor(labels, levels = labels, ordered = TRUE))
}

theme_barsberight <- function() {
  # We looked at this earlier
}

make_graphs <- function(df = replace_data()) {
  # Create and assemble 5 plots,
  # making use of theme_barsberight()
  # for all of them!
}

Parameterising the unknown

The glorious gory details

  • What if the sum isn’t 100?
    • Easy, fill in the rest with NAs!
    • What if it’s greater than 100…?
    • Wait, what if it isn’t a percentage?
make_graphs <- function(df = replace_data()) {
  
  empty <- ggplot2::ggplot(df,
                           ggplot2::aes(x = labels,
                                        y = numbers)) +
    ggplot2::labs(title = toupper("It looked\nlike this")) +
    theme_barsberight()
  
  bars <- ggplot2::ggplot(df,
                          ggplot2::aes(x = labels,
                                       y = numbers,
                                       fill = colours),
                          colour = "#FFFFFF") +
    ggplot2::geom_bar(stat = "identity",
                      linewidth = 1) +
    ggtext::geom_textbox(ggplot2::aes(label = paste0(labels,
                                                     "<span style=font-size:16pt><br>",
                                                     numbers,
                                                     unit,
                                                     "</span>")),
                         vjust = 0,
                         size = 3.5,
                         family = "Trebuchet MS",
                         halign = 0.5,
                         hjust = 0.5,
                         lineheight = 1.6,
                         fill = NA,
                         box.colour = NA) +
    ggplot2::labs(title = toupper("But should have looked\nmore like this")) +
    ggplot2::scale_fill_identity() +
    ggplot2::scale_y_continuous(expand = ggplot2::expansion(c(0.05, 0.25))) +
    theme_barsberight()
  
  # Setting up the waffle data
  # If the unit is 100%
  if(unique(df$unit) == "%") {
    
    if(sum(df$numbers, na.rm = TRUE) == 100) {
      
      df <- df |> 
        dplyr::mutate(waffle_proportions = numbers)
      
    } else if(sum(df$numbers, na.rm = TRUE) != 100) {
      # if the total is less than 100, show unaccounted ones
      if(sum(df$numbers, na.rm = TRUE) < 100) {
        
        df <- df |>
          dplyr::bind_rows(dplyr::tibble(labels = NA,
                                         numbers = 100 - sum(df$numbers),
                                         unit = "%",
                                         colours = "grey85")) |>
          dplyr::mutate(waffle_proportions = numbers)
        
        # If it's more than 100, bring it back to 100
      } else if(sum(df$numbers, na.rm = TRUE) > 100) {
        
        df <- df |>
          dplyr::mutate(waffle_proportions = janitor::round_half_up(numbers/sum(numbers, na.rm = TRUE) * 100))
        
      }
    }
    # If its not %, we just need proportions, regardless of whether the total equals 100
  } else {
    df <- df |>
      dplyr::mutate(waffle_proportions = janitor::round_half_up(numbers/sum(numbers, na.rm = TRUE) * 100))
  }
  
  x_coords <- y_coords <- waffle_colours <- c()
  for(prop in df$waffle_proportions) {
    x_coords <- rep(1:10, 10)
    y_coords <- sort(rep(1:10, 10))
    waffle_colours <- c(waffle_colours, rep(dplyr::filter(df, waffle_proportions == prop) |> 
                                              dplyr::select(colours) |>
                                              dplyr::pull(),
                                            prop))
  } 
  
  waffle_data <- dplyr::tibble(x_coords,
                               y_coords,
                               waffle_colours)
  
  waffle <- ggplot2::ggplot(waffle_data,
                            ggplot2::aes(fill = waffle_colours,
                                         x = x_coords,
                                         y = y_coords)) +
    ggplot2::geom_point(shape = 22,
                        colour = "#FFFFFF",
                        stroke = 1,
                        size = 6) +
    ggplot2::labs(title = toupper("\nlike this")) +
    ggplot2::scale_fill_identity() +
    ggplot2::coord_fixed() +
    ggplot2::scale_y_continuous(expand = ggplot2::expansion(c(0.1, 0.1))) +
    ggplot2::scale_x_continuous(expand = ggplot2::expansion(c(0.1, 0.1))) +
    theme_barsberight()
  
  donut <- ggplot2::ggplot(df,
                           ggplot2::aes(x = 1,
                                        y = numbers, 
                                        fill = labels)) +
    ggplot2::geom_bar(stat = "identity", position = "stack",
                      linewidth = 1, 
                      colour = "#FFFFFF",
                      show.legend = FALSE) +
    ggplot2::labs(title = toupper(" - It could also have looked - \nOr this")) +
    ggplot2::scale_fill_manual(values = df$colours, na.value = "grey85") +
    ggplot2::scale_x_continuous(limits = c(-0.25, 1.5)) +
    ggplot2::coord_polar(theta = "y", direction = -1) +
    theme_barsberight()
  
  x_coords <- y_coords <- macaron_colours <- c()
  
  for(num in df$numbers) {
    x_coords <- c(x_coords, runif(num, -1, 1))
    y_coords <- c(y_coords, runif(num, -1, 1))
    macaron_colours <- c(macaron_colours, rep(dplyr::filter(df, numbers == num) |> 
                                                dplyr::select(colours) |>
                                                dplyr::pull(),
                                              num))
  } 
  
  macaron_data <- dplyr::tibble(x_coords,
                                y_coords,
                                macaron_colours)
  
  macarons <- ggplot2::ggplot(macaron_data,
                              ggplot2::aes(x = x_coords,
                                           y = y_coords, 
                                           fill = macaron_colours)) +
    ggplot2::geom_point(shape = 21, colour = "#FFFFFF",
                        size = 6, alpha = 0.9,
                        stroke = 1) +
    ggplot2::labs(title = toupper("\nOr this"),
                  caption = "#BarsBeRight\nwww.cararthompson.com/apps/barsberight") +
    ggplot2::scale_y_continuous(limits = c(-1, 1), expand = ggplot2::expansion(c(0.1, 0.1))) +
    ggplot2::scale_x_continuous(limits = c(-1, 1), expand = ggplot2::expansion(c(0.1, 0.1))) +
    ggplot2::scale_fill_identity() +
    ggplot2::coord_fixed() +
    theme_barsberight()
  
  library(patchwork)
  
  row1 <- empty + bars
  row2 <- waffle + donut + macarons
  
  row1 / row2 +
    plot_layout(heights = c(3, 2)) &
    plot_annotation(theme = ggplot2::theme(plot.background = ggplot2::element_rect(color  = '#FFFFFF50', linewidth = 0, fill ="#FFFFFF70"),
                                           plot.margin = ggplot2::unit(c(5, 5, 5, 5), "pt")))
  
}

Parameterising the unknown

The glorious gory details

  • What if they input the same colour twice?
    • You mean like in the original?
  • Why won’t the waffle plot package load?
    • Create a waffle from scratch!
make_graphs <- function(df = replace_data()) {
  
  empty <- ggplot2::ggplot(df,
                           ggplot2::aes(x = labels,
                                        y = numbers)) +
    ggplot2::labs(title = toupper("It looked\nlike this")) +
    theme_barsberight()
  
  bars <- ggplot2::ggplot(df,
                          ggplot2::aes(x = labels,
                                       y = numbers,
                                       fill = colours),
                          colour = "#FFFFFF") +
    ggplot2::geom_bar(stat = "identity",
                      linewidth = 1) +
    ggtext::geom_textbox(ggplot2::aes(label = paste0(labels,
                                                     "<span style=font-size:16pt><br>",
                                                     numbers,
                                                     unit,
                                                     "</span>")),
                         vjust = 0,
                         size = 3.5,
                         family = "Trebuchet MS",
                         halign = 0.5,
                         hjust = 0.5,
                         lineheight = 1.6,
                         fill = NA,
                         box.colour = NA) +
    ggplot2::labs(title = toupper("But should have looked\nmore like this")) +
    ggplot2::scale_fill_identity() +
    ggplot2::scale_y_continuous(expand = ggplot2::expansion(c(0.05, 0.25))) +
    theme_barsberight()
  
  # Setting up the waffle data
  # If the unit is 100%
  if(unique(df$unit) == "%") {
    
    if(sum(df$numbers, na.rm = TRUE) == 100) {
      
      df <- df |> 
        dplyr::mutate(waffle_proportions = numbers)
      
    } else if(sum(df$numbers, na.rm = TRUE) != 100) {
      # if the total is less than 100, show unaccounted ones
      if(sum(df$numbers, na.rm = TRUE) < 100) {
        
        df <- df |>
          dplyr::bind_rows(dplyr::tibble(labels = NA,
                                         numbers = 100 - sum(df$numbers),
                                         unit = "%",
                                         colours = "grey85")) |>
          dplyr::mutate(waffle_proportions = numbers)
        
        # If it's more than 100, bring it back to 100
      } else if(sum(df$numbers, na.rm = TRUE) > 100) {
        
        df <- df |>
          dplyr::mutate(waffle_proportions = janitor::round_half_up(numbers/sum(numbers, na.rm = TRUE) * 100))
        
      }
    }
    # If its not %, we just need proportions, regardless of whether the total equals 100
  } else {
    df <- df |>
      dplyr::mutate(waffle_proportions = janitor::round_half_up(numbers/sum(numbers, na.rm = TRUE) * 100))
  }
  
  x_coords <- y_coords <- waffle_colours <- c()
  for(prop in df$waffle_proportions) {
    x_coords <- rep(1:10, 10)
    y_coords <- sort(rep(1:10, 10))
    waffle_colours <- c(waffle_colours, rep(dplyr::filter(df, waffle_proportions == prop) |> 
                                              dplyr::select(colours) |>
                                              dplyr::pull(),
                                            prop))
  } 
  
  waffle_data <- dplyr::tibble(x_coords,
                               y_coords,
                               waffle_colours)
  
  waffle <- ggplot2::ggplot(waffle_data,
                            ggplot2::aes(fill = waffle_colours,
                                         x = x_coords,
                                         y = y_coords)) +
    ggplot2::geom_point(shape = 22,
                        colour = "#FFFFFF",
                        stroke = 1,
                        size = 6) +
    ggplot2::labs(title = toupper("\nlike this")) +
    ggplot2::scale_fill_identity() +
    ggplot2::coord_fixed() +
    ggplot2::scale_y_continuous(expand = ggplot2::expansion(c(0.1, 0.1))) +
    ggplot2::scale_x_continuous(expand = ggplot2::expansion(c(0.1, 0.1))) +
    theme_barsberight()
  
  donut <- ggplot2::ggplot(df,
                           ggplot2::aes(x = 1,
                                        y = numbers, 
                                        fill = labels)) +
    ggplot2::geom_bar(stat = "identity", position = "stack",
                      linewidth = 1, 
                      colour = "#FFFFFF",
                      show.legend = FALSE) +
    ggplot2::labs(title = toupper(" - It could also have looked - \nOr this")) +
    ggplot2::scale_fill_manual(values = df$colours, na.value = "grey85") +
    ggplot2::scale_x_continuous(limits = c(-0.25, 1.5)) +
    ggplot2::coord_polar(theta = "y", direction = -1) +
    theme_barsberight()
  
  x_coords <- y_coords <- macaron_colours <- c()
  
  for(num in df$numbers) {
    x_coords <- c(x_coords, runif(num, -1, 1))
    y_coords <- c(y_coords, runif(num, -1, 1))
    macaron_colours <- c(macaron_colours, rep(dplyr::filter(df, numbers == num) |> 
                                                dplyr::select(colours) |>
                                                dplyr::pull(),
                                              num))
  } 
  
  macaron_data <- dplyr::tibble(x_coords,
                                y_coords,
                                macaron_colours)
  
  macarons <- ggplot2::ggplot(macaron_data,
                              ggplot2::aes(x = x_coords,
                                           y = y_coords, 
                                           fill = macaron_colours)) +
    ggplot2::geom_point(shape = 21, colour = "#FFFFFF",
                        size = 6, alpha = 0.9,
                        stroke = 1) +
    ggplot2::labs(title = toupper("\nOr this"),
                  caption = "#BarsBeRight\nwww.cararthompson.com/apps/barsberight") +
    ggplot2::scale_y_continuous(limits = c(-1, 1), expand = ggplot2::expansion(c(0.1, 0.1))) +
    ggplot2::scale_x_continuous(limits = c(-1, 1), expand = ggplot2::expansion(c(0.1, 0.1))) +
    ggplot2::scale_fill_identity() +
    ggplot2::coord_fixed() +
    theme_barsberight()
  
  library(patchwork)
  
  row1 <- empty + bars
  row2 <- waffle + donut + macarons
  
  row1 / row2 +
    plot_layout(heights = c(3, 2)) &
    plot_annotation(theme = ggplot2::theme(plot.background = ggplot2::element_rect(color  = '#FFFFFF50', linewidth = 0, fill ="#FFFFFF70"),
                                           plot.margin = ggplot2::unit(c(5, 5, 5, 5), "pt")))
  
}

Give it a go!

Having said all that…

It’s a win for Dataviz Design Systems!

Let’s chat!

Let’s chat!

#BarsBeRight



Dataviz Design Questions

My Website