Deep dive: themes

Lecture 09

Dr. Benjamin Soltoff

Cornell University
INFO 3312/5312 - Spring 2025

February 20, 2025

Announcements

Announcements

  • Homework 03 due next week
  • Revised project 01 proposals due tonight by 11:59pm

Visualization critique

Gulf of ???

Themes

The {ggplot2} default

Alternative theme

library(ggthemes)

ggplot(penguins, aes(
  x = bill_depth_mm,
  y = bill_length_mm,
  color = species
)) +
  geom_point(size = 2) +
  theme_tufte()

And this!

library(ggthemes)

ggplot(penguins, aes(
  x = bill_depth_mm,
  y = bill_length_mm,
  color = species
)) +
  geom_point(size = 2) +
  theme_solarized()

Or this!

library(ggthemes)

ggplot(penguins, aes(
  x = bill_depth_mm,
  y = bill_length_mm,
  color = species
)) +
  geom_point(size = 2) +
  scale_color_economist() +
  theme_economist()

The anatomy of a ggplot() theme

Theme system

Theme elements

Each element in the plot can be targeted

  • Plot title = plot.title
  • Grid lines = panel.grid
  • Legend background = legend.background

Use special functions to manipulate specific elements

  • Text-based things = element_text()
  • Rectangular things (backgrounds) = element_rect()
  • Line-based things (axis lines, grid lines) = element_line()
  • Disable element completely = element_blank()

How to learn theme()

  • The theme() function has 136 possible arguments
  • You can get hyper-specific with things like axis.ticks.length.x.bottom
  • The only way to learn how to use theme() is to use it and tinker with it

{gapminder}

library(gapminder)
gapminder
# A tibble: 1,704 × 6
   country     continent  year lifeExp      pop gdpPercap
   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
 1 Afghanistan Asia       1952    28.8  8425333      779.
 2 Afghanistan Asia       1957    30.3  9240934      821.
 3 Afghanistan Asia       1962    32.0 10267083      853.
 4 Afghanistan Asia       1967    34.0 11537966      836.
 5 Afghanistan Asia       1972    36.1 13079460      740.
 6 Afghanistan Asia       1977    38.4 14880372      786.
 7 Afghanistan Asia       1982    39.9 12881816      978.
 8 Afghanistan Asia       1987    40.8 13867957      852.
 9 Afghanistan Asia       1992    41.7 16317921      649.
10 Afghanistan Asia       1997    41.8 22227415      635.
# ℹ 1,694 more rows

Basic plot

gapminder_filtered <- gapminder |>
  filter(year > 2000)

base_plot <- ggplot(
  data = gapminder_filtered,
  mapping = aes(
    x = gdpPercap, y = lifeExp,
    color = continent, size = pop
  )
) +
  geom_point() +
  # Use dollars, and get rid of the cents part (i.e. $300 instead of $300.00)
  scale_x_log10(labels = label_dollar(scale_cut = cut_short_scale())) +
  # Format with commas
  scale_size_continuous(labels = label_comma()) +
  # Use dark 3
  scale_color_discrete_qualitative(palette = "Dark 3") +
  labs(
    x = "GDP per capita", y = "Life expectancy",
    color = "Continent", size = "Population",
    title = "Here's a cool title",
    subtitle = "And here's a neat subtitle",
    caption = "Source: The Gapminder Project"
  ) +
  facet_wrap(facets = vars(year)) +
  theme_grey()

base_plot

Change the theme

base_plot +
  theme_minimal()

Create a custom theme

# change base font and size
theme_pretty <- function(base_family = "Atkinson Hyperlegible", base_size = 14, ...) {
  theme_minimal(base_family = base_family, base_size = base_size, ...) +
    theme(
      # Remove minor grid lines
      panel.grid.minor = element_blank(),
      # Bold, bigger title
      plot.title = element_text(face = "bold", size = rel(1.7)),
      # Plain, slightly bigger subtitle that is grey
      plot.subtitle = element_text(face = "plain", size = rel(1.3), color = "grey70"),
      # Italic, smaller, grey caption that is left-aligned
      plot.caption = element_text(
        face = "italic", size = rel(0.7),
        color = "grey70", hjust = 0
      ),
      # Bold legend titles
      legend.title = element_text(face = "bold"),
      # Bold, slightly larger facet titles that are left-aligned for the sake of repetition
      strip.text = element_text(face = "bold", size = rel(1.1), hjust = 0),
      # Bold axis titles
      axis.title = element_text(face = "bold"),
      # Add some space above the x-axis title and make it left-aligned
      axis.title.x = element_text(margin = margin(t = 10), hjust = 0),
      # Add some space to the right of the y-axis title and make it top-aligned
      axis.title.y = element_text(margin = margin(r = 10), hjust = 1),
      # Add a light grey background to the facet titles, with no borders
      strip.background = element_rect(fill = "grey90", color = NA),
      # Add a thin grey border around all the plots to tie in the facet titles
      panel.border = element_rect(color = "grey90", fill = NA)
    )
}

Applied to existing plot

base_plot +
  theme_pretty()

Render in a document

base_plot +
  theme_pretty(base_size = 11)

Applied to another plot

library(palmerpenguins)

ggplot(
  data = drop_na(penguins, sex),
  mapping = aes(x = bill_length_mm, y = body_mass_g, color = str_to_title(sex))
) +
  geom_point(size = 3, alpha = 0.5) +
  scale_color_discrete_qualitative(palette = "Dark 3") +
  scale_y_continuous(labels = label_comma()) +
  facet_wrap(facets = vars(species)) +
  labs(
    x = "Bill length (mm)", y = "Body mass (g)", color = "Sex",
    title = "Gentoo penguins are the largest",
    subtitle = "But females are typically smaller than males",
    caption = "Here's a caption"
  ) +
  theme_pretty()

A note on custom fonts

Custom fonts in {ggplot2}

  • Rendering text and custom fonts has been historically difficult in R (and many other programming languages)
  • Requires unifying the graphics device, operating system, text rendering, and R
  • Several approaches exist with {ggplot2} to simplify this process

{ragg}

  • {ragg} is a package that provides a consistent way to render fonts across different devices
  • Install fonts directly on system and use {ragg} as the graphics device
  • Many great uses for this workflow! But it requires the ability to directly install fonts on the device…

Can’t install fonts directly on system?

Use {systemfonts} and require_font() to download font automatically from Google Fonts or Font Squirrel.

Ensure graphs are rendered using {ragg}

  • ggsave() automatically uses {ragg} as of {ggplot2} 3.3.4
  • In Quarto, set the dev chunk option
knitr:
  opts_chunk:
    dev: ragg_png

Application exercise

ae-08

Instructions

  • Go to the course GitHub org and find your ae-08 (repo name will be suffixed with your GitHub name).
  • Clone the repo in RStudio, run renv::restore() to install the required packages, open the Quarto document in the repo, and follow along and complete the exercises.
  • Render, commit, and push your edits by the AE deadline – end of the day

Wrap up

Wrap up

  • {ggplot2} uses theming to control the visual appearance of charts
  • Use custom themes to apply consistent styles across multiple plots

Jazz or Not Jazz?