I have a list of data frames that I use to make a list of ggplot
s, 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()
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).
@Claus Wilke's solution got the job done, but I was inspired to write a wrapper function around draw_label
to get the styling necessary to mimic a theme element. I'm posting here in case it's of use to anybody else, though this might be a weird use case.
This function takes a theme function or, if theme
is omitted, gets the current theme from theme_get
. It also takes the name of a theme element, such as "plot.title"
, from which it gets the styling with calc_element
. All the requisite arguments are passed to cowplot::draw_label
, along with anything else in ...
such as x
or hjust
.
library(tidyverse)
library(cowplot)
draw_label_theme <- function(label, theme = NULL, element = "text", ...) {
if (is.null(theme)) {
theme <- ggplot2::theme_get()
}
if (!element %in% names(theme)) {
stop("Element must be a valid ggplot theme element name")
}
elements <- ggplot2::calc_element(element, theme)
cowplot::draw_label(label,
fontfamily = elements$family,
fontface = elements$face,
colour = elements$color,
size = elements$size,
...
)
}
title <- ggdraw() +
draw_label_theme("Socio-economic measures",
theme = theme_georgia(), element = "plot.title",
x = 0.05, hjust = 0, vjust = 1)
subtitle <- ggdraw() +
draw_label_theme("By census tract, 2016",
theme = theme_georgia(), element = "plot.subtitle",
x = 0.05, hjust = 0, vjust = 1)
Now the only hard part is lining things up neatly in the rel_heights
, which I could mess around with more here. There might be positioning information to pull out of the theme and use to set the heights.
plot_grid(title, subtitle, gridded, ncol = 1, rel_heights = c(0.1, 0.1, 1))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With