如何为ggplot2的facet_wrap添加位置二级列标题?
实现分位置的二级列标题绘图
需求说明
现有包含8个站点(A-H)的数据,站点分属North和South两个区域,已用facet_wrap()生成2行4列的子图,需要为左两列(North站点:A、B、C、D)添加统一的"North"标题,右两列(South站点:E、F、G、H)添加统一的"South"标题,实现跨两行的二级列标题效果。
解决方案1:使用patchwork组合子图
这种方法逻辑清晰,通过拆分数据分别绘图后组合,适合快速实现需求:
library(ggplot2) library(dplyr) library(patchwork) # 生成示例数据 set.seed(123) df <- data.frame(matrix(ncol = 4, nrow = 24)) colnames(df)[1:4] <- c('location','site','x','y') df$location <- rep(c('North','North','North','South','South','South'),4) df$site <- c('A','A','A','E','E','E','B','B','B','F','F','F', 'C','C','C','G','G','G','D','D','D','H','H','H') df$x <- rep(seq(0,12,4),6) df$y <- rnorm(24,50,20) # 定义通用绘图函数 plot_site <- function(data) { ggplot(data, aes(x = x, y = y)) + geom_point() + geom_line() + scale_x_continuous(breaks = seq(0,12,3), limits = c(0,12)) + scale_y_continuous(breaks = seq(0, max(df$y),5)) + theme_bw() + facet_wrap(~site, nrow = 2) + theme(strip.background = element_rect(fill = "white")) } # 拆分数据并指定站点顺序 north_df <- df %>% filter(location == "North") %>% mutate(site = factor(site, levels = c("A", "B", "C", "D"))) south_df <- df %>% filter(location == "South") %>% mutate(site = factor(site, levels = c("E", "F", "G", "H"))) # 绘制分区域子图并添加标题 p_north <- plot_site(north_df) + ggtitle("North") + theme(plot.title = element_text(hjust = 0.5, face = "bold")) p_south <- plot_site(south_df) + ggtitle("South") + theme(plot.title = element_text(hjust = 0.5, face = "bold")) # 组合图表,左右两列宽度一致 p_north + p_south + plot_layout(ncol = 2, widths = c(1,1))
解决方案2:修改ggplot的gtable对象(无需额外安装包)
这种方法直接修改ggplot生成的底层表格对象,适合不想引入新包的场景:
library(ggplot2) library(dplyr) library(gtable) library(grid) # 生成示例数据 set.seed(123) df <- data.frame(matrix(ncol = 4, nrow = 24)) colnames(df)[1:4] <- c('location','site','x','y') df$location <- rep(c('North','North','North','South','South','South'),4) df$site <- c('A','A','A','E','E','E','B','B','B','F','F','F', 'C','C','C','G','G','G','D','D','D','H','H','H') df$x <- rep(seq(0,12,4),6) df$y <- rnorm(24,50,20) # 绘制基础facet_wrap图表 p <- df %>% mutate(site = factor(site, levels = c('A','B','E','F','C','D','G','H'))) %>% ggplot(aes(x = x, y = y)) + geom_point() + geom_line() + scale_x_continuous(breaks = seq(0,12,3), limits = c(0,12)) + scale_y_continuous(breaks = seq(0,max(df$y),5)) + theme_bw() + facet_wrap(~site, nrow = 2) # 转换为gtable对象 gt <- ggplotGrob(p) # 找到顶部strip的位置,添加二级标题行 strip_row_pos <- grep("strip-b", gt$layout$name)[1] - 1 gt <- gtable_add_rows(gt, height = unit(1.2, "line"), pos = strip_row_pos) # 添加North标题(覆盖前两列) gt <- gtable_add_grob(gt, textGrob("North", gp = gpar(fontsize = 12, fontface = "bold")), t = strip_row_pos + 1, l = gt$layout$l[grep("strip-b", gt$layout$name)[1]], r = gt$layout$l[grep("strip-b", gt$layout$name)[2]]) # 添加South标题(覆盖后两列) gt <- gtable_add_grob(gt, textGrob("South", gp = gpar(fontsize = 12, fontface = "bold")), t = strip_row_pos + 1, l = gt$layout$l[grep("strip-b", gt$layout$name)[3]], r = gt$layout$l[grep("strip-b", gt$layout$name)[4]]) # 绘制最终图表 grid.newpage() grid.draw(gt)
两种方案都能实现需求:左两列显示North站点并添加统一标题,右两列显示South站点并添加统一标题,同时保留原有的scale_x_continuous()设置。
内容的提问来源于stack exchange,提问作者tassones
相关产品推荐
相关产品推荐

