RiskyViz - Dataviz tips, tricks and traps to avoid when parameterising graphs about risk

data-visualisation
r-how-to
parameterised-plots

Lightening talk for the inaugural R Consortium R!SK Conference 2026. This talk explores dataviz design principles and their implementation within ggplot, along with important safety features we can build in, to maximise the impact and reliability of our visuals in communicating risk.

Published

February 19, 2026

Recording

Slides

View full screen

Final code

Code
library(ggplot2)
make_welcome_plot <- function(df = penguins, zoo_keeper = "anyone") {
  threshold <- dplyr::case_when(
    zoo_keeper == "Sam" ~ 40,
    zoo_keeper == "Meg" ~ 50,
    zoo_keeper == "Ron" ~ 60,
    .default = 50
  )

  keeper_emoji <- dplyr::case_when(
    zoo_keeper == "Sam" ~ "๐Ÿ™€",
    zoo_keeper == "Meg" ~ "๐Ÿฆธ๐Ÿพโ€โ™€๏ธ",
    zoo_keeper == "Ron" ~ "๐Ÿ‘จ๐Ÿป",
    .default = "๐Ÿง‘โ€๐ŸŒพ"
  )

  df |>
    ggplot() +
    annotate(
      "rect",
      xmin = threshold,
      xmax = Inf,
      ymin = -Inf,
      ymax = Inf,
      fill = alpha("skyblue", 0.3)
    ) +
    geom_vline(xintercept = threshold, colour = "#2c3d4f", linewidth = 0.2) +
    ggtext::geom_textbox(
      data = data.frame(),
      aes(x = threshold, y = Inf, label = paste0("Beaks > ", threshold, "mm")),
      fill = NA,
      box.colour = NA,
      hjust = 0,
      vjust = 1,
      box.padding = unit(10, "pt"),
      family = "Noah",
      colour = "#3b4046",
      size = 5
    ) +
    ggbeeswarm::geom_quasirandom(
      aes(
        x = bill_len,
        y = species,
        shape = species,
        fill = flipper_len
      ),
      size = 4,
      colour = "#2c3d4f",
      stroke = 0.2
    ) +
    labs(
      x = "Beak length",
      title = paste0(
        keeper_emoji,
        " ",
        dplyr::case_when(
          zoo_keeper %in% c("Sam", "Meg", "Ron") ~ zoo_keeper,
          .default = "Keeper"
        ),
        ", you are about to welcome ",
        nrow(df),
        " penguins"
      ),
      subtitle = paste0(
        "There are **",
        verbaliseR::pluralise(
          sum(df$bill_len > threshold, na.rm = TRUE),
          word = "penguin"
        ),
        " with scary beaks**. Please look out for the ones on the red (darker) end of the spectrum; they have longer flippers so will need more support settling in."
      ),
      fill = "Predicted need for support"
    ) +
    scale_x_continuous(
      label = function(x) paste0(x, "mm"),
      limits = c(
        # Using range in the full penguin dataframe
        min(penguins$bill_len, na.rm = TRUE),
        max(penguins$bill_len, na.rm = TRUE)
      )
    ) +
    scale_shape_manual(
      guide = "none",
      values = c("Adelie" = 21, "Chinstrap" = 24, "Gentoo" = 22)
    ) +
    scale_fill_gradient2(
      low = "#a5dc94",
      mid = "#a7a7a7",
      high = "#e0361d",
      midpoint = mean(penguins$flipper_len, na.rm = TRUE),
      limits = c(
        min(penguins$flipper_len, na.rm = TRUE),
        max(penguins$flipper_len, na.rm = TRUE)
      ),
      breaks = c(
        min(penguins$flipper_len, na.rm = TRUE),
        max(penguins$flipper_len, na.rm = TRUE)
      ),
      labels = c("low", "high")
    ) +
    theme(
      text = element_text(family = "Noah", colour = "#3b4046"),
      plot.subtitle = marquee::element_marquee(
        width = 1,
        family = "Noah",
        size = rel(1.2)
      ),
      plot.title = element_text(
        face = "bold",
        size = rel(1.6),
        colour = "#1A242F"
      ),
      legend.title = marquee::element_marquee(hjust = 0.5),
      axis.title.y = element_blank(),
      panel.grid = element_line(colour = "white"),
      plot.background = element_rect(fill = "#fafafa", colour = "#fafafa"),
      plot.margin = margin_auto(40),
      axis.title.x = element_text(hjust = 1, margin = margin(10, 0, 0, 0)),
      legend.position = "top",
      plot.title.position = "plot",
      legend.title.position = "top",
      legend.justification = 0.5,
      legend.key.width = unit(3.6, "lines")
    )
}

Resources I mentioned

Code along

If you want to retrace our steps more gradually towards a parameterised plot function, hereโ€™s a code-along workshop I did for RMedicine in which we built a similar function and added a few extra bells and whistles.

๐Ÿ‘‰ Visualise, Optimise, Parameterise!

Happy datavizing!

Reuse

Citation

For attribution, please cite this work as:
โ€œRiskyViz - Dataviz Tips, Tricks and Traps to Avoid When Parameterising Graphs about Risk.โ€ 2026. February 19, 2026. https://www.cararthompson.com/talks/riskyviz/.