ggplot2: Creating themed title, subtitle with cowplot

camille picture camille · Jun 21, 2018 · Viewed 7.5k times · Source

I have a list of data frames that I use to make a list of ggplots, and then assemble into a grid of plots with cowplot. I need to then attach a shared title, subtitle, and caption. I want to do this in a way that these labels will have the same theme elements (size, fontface, etc) as if they were created by labs instead of cowplot::draw_label.

In my real situation, I have several choropleths that each have their own units and scales, which is why I can't just facet, but instead need to build the plots independent of one another.

Here's a simplified version of the data, and the packages I have loaded:

library(tidyverse)
library(cowplot)

dfs <- list(
  adults_no_diploma = structure(list(
    tract = c("09003405100", "09003405200", "09003405300", "09003405401", "09003405402", "09003405500", "09003405600", "09003405700", "09003405800", "09003405900"), 
    value = c(0.08, 0.108, 0.095, 0.099, 0.105, 0.103, 0.161, 0.279, 0.056, 0.055)), 
    row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame")), 
  severe_cost_burden = structure(list(
    tract = c("09003405100", "09003405200", "09003405300", "09003405401", "09003405402", "09003405500", "09003405600", "09003405700", "09003405800", "09003405900"), 
    value = c(0.128, 0.147, 0.165, 0.1, 0.151, 0.11, 0.179, 0.184, 0.14, 0.038)), 
    row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"))
)

I'm working with a custom theme (again, a simplified version):

theme_georgia <- function(...) {
  theme_gray(base_family = "Georgia", ...) + 
    theme(plot.title = element_text(face = "bold"))
}

plots <- dfs %>%
  imap(~{
    ggplot(.x, aes(x = value)) + 
      geom_density() + 
      ggtitle(.y) +
      theme_georgia()
    })

gridded <- plot_grid(plotlist = plots, nrow = 1)

Following the cowplot annotations vignette, I can make a title with ggdraw() + draw_label("Socio-economic measures") and manually set things like font size, but what I'd prefer is to somehow define that label as a title; that is, the theme would apply everything in plot.title to it, and the same for creating a subtitle and caption.

My current workaround is to make an empty ggplot with labs for the title and subtitle, do the same for a caption, and stack them vertically with cowplot::plot_grid.

title_gg <- ggplot() + 
  labs(title = "Socio-economic measures", subtitle = "By census tract, 2016") + 
  theme_georgia()
plot_grid(title_gg, gridded, ncol = 1, rel_heights = c(0.15, 1))

The workaround is okay, but I like the neatness and alignment you get with draw_label. I could instead add in these theme elements one by one to mimic a title:

title_theme <- ggdraw() +
  draw_label("Socio-economic measures", 
             fontfamily = theme_georgia()$text$family, 
             fontface = theme_georgia()$plot.title$face, x = 0.05, hjust = 0)
plot_grid(title_theme, gridded, ncol = 1, rel_heights = c(0.2, 1))

My question is whether there's any way I might combine these approaches to pull all the relevant theme elements and feed them to draw_label quickly, or somehow tell draw_label that this thing is a title and should get title theme elements, and this other thing is a subtitle, and so on. I imagine some sort of magic like this:

ggdraw() + 
  draw_label("Socio-economic measures", theme_georgia()$plot.title_elements)

or:

ggdraw() + 
  draw_label("Socio-economic measures", type = "title") + theme_georgia()

Answer

Claus Wilke picture Claus Wilke · Jun 21, 2018

Like so?

library(ggplot2)
library(cowplot)

theme_georgia <- function(...) {
  theme_gray(base_family = "Georgia", ...) + 
    theme(plot.title = element_text(face = "bold"))
}


title_theme <- calc_element("plot.title", theme_georgia())

ggdraw() + 
  draw_label(
    "Socio-economic measures",
    fontfamily = title_theme$family,
    fontface = title_theme$face,
    size = title_theme$size
  )

Created on 2018-06-21 by the reprex package (v0.2.0).