Schools and names of spells from the Dungeons and Dragons (2024)

A simple text chart using colour and alpha as aesthetics to create a ranking effect.

#TidyTuesday
Author

Aditya Dahiya

Published

December 19, 2024

About the Data

The Dungeons and Dragons Spells (2024) dataset explores magical spells from the Dungeons and Dragons Free Rules (2024 edition). Curated by Jon Harmon, it provides details on each spell, including level, school of magic, casting requirements, duration, and more.

The dataset is accessible via the tidytuesdayR package or directly from the TidyTuesday GitHub repository. It offers opportunities to analyze class-specific spellcasting options, concentration requirements, and patterns in descriptions, while encouraging practice with tools like Shiny or the tidyverse.

Figure 1: A simple text-chart of spells in different schools of Dungeons and Dragons (2024) listed by their strength / level from top to bottom. Notice the use of colour and alpha as aesthetics to create an effect.

How I made this graphic?

Loading required libraries, data import & creating custom functions.

Code
# Data Import and Wrangling Tools
library(tidyverse)            # All things tidy

# Final plot tools
library(scales)               # Nice Scales for ggplot2
library(fontawesome)          # Icons display in ggplot2
library(ggtext)               # Markdown text support for ggplot2
library(showtext)             # Display fonts in ggplot2
library(colorspace)           # Lighten and Darken colours
library(patchwork)            # Compiling Plots

spells <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/main/data/2024/2024-12-17/spells.csv') |> 
  janitor::clean_names()

Visualization Parameters

Code
# Font for titles
font_add_google("Eagle Lake",
  family = "title_font"
) 

# Font for the caption
font_add_google("Saira Extra Condensed",
  family = "caption_font"
) 

# Font for plot text
font_add_google("Nova Oval",
  family = "body_font"
) 

mypal <- c("#990099", "#009900")
showtext_auto()


# A base Colour
bg_col <- "black"
seecolor::print_color(bg_col)

panel_col <- lighten(bg_col, 0.2)
seecolor::print_color(panel_col)

# Colour for highlighted text
text_hil <- "white"
seecolor::print_color(text_hil)

# Colour for the text
text_col <- darken("white", 0.1)
seecolor::print_color(text_col)


# Define Base Text Size
bts <- 90 

# Caption stuff for the plot
sysfonts::font_add(
  family = "Font Awesome 6 Brands",
  regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf")
)
github <- "&#xf09b"
github_username <- "aditya-dahiya"
xtwitter <- "&#xe61b"
xtwitter_username <- "@adityadahiyaias"
social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username}  </span>")
social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")
plot_caption <- paste0(
  "**Data:** Jon Harmon, Dungeons and Dragons Free Rules (2024 edition)", 
  " |  **Code:** ", 
  social_caption_1, 
  " |  **Graphics:** ", 
  social_caption_2
  )
rm(github, github_username, xtwitter, 
   xtwitter_username, social_caption_1, 
   social_caption_2)

# Add text to plot-------------------------------------------------
plot_title <- "Dungeons and Dragons Spells (2024)"

plot_subtitle <- ""

Exploratory Data Analysis and Wrangling

Code
# spells |>
#   summarytools::dfSummary() |>
#   summarytools::view()

df <- spells |> 
  pivot_longer(
    cols = 4:11,
    names_to = "character",
    values_to = "value"
  ) |> 
  filter(value) |> 
  select(name, level, school, character, value) |> 
  group_by(character) |> 
  distinct(name, level) |> 
  arrange(character)

df1 <- spells |> 
  select(school, name, level) |> 
  mutate(level = as_factor(level)) |> 
  arrange(desc(level)) |> 
  group_by(school, level) |>
  slice_head(n = 5) |>
  mutate(
    school = str_to_title(school),
    nudge_var = row_number(),
    level = as.numeric(level),
    nudge_var = case_when(
      nudge_var == 1 ~ level,
      nudge_var == 2 ~ level + 0.2,
      nudge_var == 3 ~ level - 0.2,
      nudge_var == 4 ~ level + 0.4,
      nudge_var == 5 ~ level - 0.4,
      .default = level
    )
  )

The Base Plot

Code
g <- df1 |> 
  ggplot(
    mapping = aes(
      x = school, 
      y = nudge_var,
      label = name,
      colour = school,
      alpha = nudge_var
    )
  ) +
  geom_text(
    family = "caption_font",
    size = bts / 4,
    check_overlap = TRUE
  ) +
  scale_y_continuous(
    breaks = 1:10,
    labels = 1:10
  ) +
  scale_x_discrete(
    position = "top"
  ) +
  scale_alpha(
    range = c(0.3, 1)
  ) +
  paletteer::scale_colour_paletteer_d("vapoRwave::vapoRwave") +
  labs(
    title = plot_title,
    subtitle = plot_subtitle,
    caption = plot_caption,
    y = "Strength / Level of the Spell",
    x = "Schools"
  ) +
  theme_minimal(
    base_family = "body_font",
    base_size = bts
  ) +
  theme(
    # Overall
    plot.background = element_rect(
      fill = "transparent",
      colour = "transparent"
    ),
    panel.background = element_rect(
      fill = "transparent",
      colour = "transparent"
    ),
    plot.title.position = "plot",
    legend.position = "none",
    plot.margin = margin(5,5,5,5, "mm"),
    text = element_text(
      colour = text_col,
      lineheight = 0.3,
      hjust = 0.5,
      vjust = 0.5,
      margin = margin(0,0,0,0, "mm")
    ),
    panel.grid = element_blank(),
    
    # Labels
    plot.title = element_text(
      colour = text_hil,
      size = 2 * bts,
      margin = margin(10,0,5,0, "mm"),
      hjust = 0.5
    ),
    plot.subtitle = element_text(
      margin = margin(5,0,0,0, "mm"),
      hjust = 0.5
    ),
    plot.caption = element_textbox(
      family = "caption_font",
      colour = text_hil,
      margin = margin(0,0,0,0, "mm"),
      hjust = 0.5,
      size = bts * 0.6
    ),
    
    # Axes
    axis.ticks.length = unit(0, "mm"),
    axis.ticks = element_blank(),
    axis.text.x = element_text(
      family = "caption_font",
      colour = text_col,
      size = 1 * bts
    ),
    axis.text.y = element_text(
      size = 2 * bts,
      colour = text_col,
      family = "body_font"
    )
  )

ggsave(
  filename = here::here(
    "data_vizs",
    "tidy_dungeons_dragons.png"
  ),
  plot = g,
  width = 400,
  height = 500,
  units = "mm",
  bg = bg_col
)

Savings the thumbnail for the webpage

Code
# Saving a thumbnail

library(magick)
# Saving a thumbnail for the webpage
image_read(here::here("data_vizs", 
                      "tidy_dungeons_dragons.png")) |> 
  image_resize(geometry = "x400") |> 
  image_write(
    here::here(
      "data_vizs", 
      "thumbnails", 
      "tidy_dungeons_dragons.png"
    )
  )

Session Info

Code
# Data Import and Wrangling Tools
library(tidyverse)            # All things tidy

# Final plot tools
library(scales)               # Nice Scales for ggplot2
library(fontawesome)          # Icons display in ggplot2
library(ggtext)               # Markdown text support for ggplot2
library(showtext)             # Display fonts in ggplot2
library(colorspace)           # Lighten and Darken colours
library(patchwork)            # Compiling Plots


sessioninfo::session_info()$packages |> 
  as_tibble() |> 
  select(package, 
         version = loadedversion, 
         date, source) |> 
  arrange(package) |> 
  janitor::clean_names(
    case = "title"
  ) |> 
  gt::gt() |> 
  gt::opt_interactive(
    use_search = TRUE
  ) |> 
  gtExtras::gt_theme_espn()
Table 1: R Packages and their versions used in the creation of this page and graphics