ggplot2: 使用cowplot创建具有主题的标题和副标题

11
我有一组数据框,用它们制作了ggplot列表,并使用cowplot组装成一个图形网格。然后,我需要添加共享标题、副标题和说明。我希望以这种方式完成,使得这些标签具有与labs创建的标签相同的主题元素(大小、字体等),而不是cowplot::draw_label
在我的实际情况中,我有几个区域地图,每个地图都有自己的单位和比例尺,这就是为什么我不能只是分面,而是需要独立构建每个地图的原因。
以下是我加载的包和数据的简化版本:
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"))
)

我正在使用一个自定义主题(简化版):

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)

cowplot注释vignette之后,我可以使用ggdraw() + draw_label("Socio-economic measures")制作标题,并手动设置字体大小,但我更喜欢以某种方式定义该标签为一个标题;也就是说,主题将应用plot.title中的所有内容,创建副标题和说明文字也是如此。
我的当前解决方法是制作一个空的ggplot,并使用labs为标题和副标题,对于说明文字也是这样,然后使用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))

这种解决方法还可以,但我更喜欢使用draw_label的整洁性和对齐性。我可以逐个添加这些主题元素来模拟标题:

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))

我的问题是是否有办法将这些方法结合起来,快速提取所有相关的主题元素并将它们馈送给draw_label,或者以某种方式告诉draw_label,这个东西是一个标题,应该获得标题主题元素,而这另一个东西是一个副标题,等等。我想象中有一些魔法可以做到这一点:
ggdraw() + 
  draw_label("Socio-economic measures", theme_georgia()$plot.title_elements)

或者:

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

很棒的回答,但有没有办法去掉灰色水平条? - Tea Tree
2个回答

7

@Claus Wilke的解决方案已经完成了任务,但我受到启发写了一个包装函数来获取所需的样式以模拟主题元素。我在这里发布,以防对别人有用,尽管这可能是一个奇怪的用例。

该函数接受一个主题函数,如果省略theme,则从theme_get获取当前主题。它还接受一个主题元素的名称,例如"plot.title",并使用calc_element获取其样式。所有必需的参数都传递给cowplot::draw_label,以及...中的任何其他参数,例如xhjust

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)

现在唯一困难的部分是要在rel_heights中整齐地排列事物,我可以在这里进行更多的尝试。也许可以从主题中提取定位信息并用于设置高度。

plot_grid(title, subtitle, gridded, ncol = 1, rel_heights = c(0.1, 0.1, 1))


6

像这样吗?

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
  )

这段文字是在2018年6月21日由reprex包(版本为v0.2.0)创建的。


是的,这太棒了!我从未见过 calc_element。如果能够将 title_theme 传递给 draw_label 而不是 fontfamilyfontface 等,那就太好了。作为作者,您知道人们是否经常使用此功能吗?我可能最终会编写一个包装函数供自己使用,因为我经常需要它,但如果对其他人有用,我可以分享它。 - camille
2
这是ggplot2在内部进行主题计算时使用的内容。我同意能够将主题元素传递给draw_label()会很好。当我编写该函数时,我并不知道自己在做什么。也许我会在某个时候对其进行修订。 - Claus Wilke
1
嘿,@Claus Wilke,我已经写了一个draw_label的包装器。如果你有需要的话,请告识我。 - camille
我讨厌人们使用library()编写代码,因为这样你就不知道每个函数来自哪个包。 - Stefano Borini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接