如何利用patchwork实现ggplot2多图共享X/Y轴且仅显示一次轴标题
问题:使用patchwork实现ggplot2 2×2栅格图的全局共享轴标题
我正在使用ggplot2和patchwork创建一个2×2的栅格图矩阵,每个图由不同数据集生成。这些图的X轴与Y轴范围一致,希望仅显示一次全局的X、Y轴标题,但使用plot_layout(axis_titles = "collect", guides = "collect")参数后,组合后的图表仍未实现轴标题共享。
可复现代码如下:
library(ggplot2) library(patchwork) # 为每个绘图创建不同数据集 df1 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df1$z <- with(df1, dnorm(x, mean = 500, sd = 50) * dnorm(y, mean = 400, sd = 50)) df2 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df2$z <- with(df2, dnorm(x, mean = 600, sd = 50) * dnorm(y, mean = 450, sd = 50)) df3 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df3$z <- with(df3, dnorm(x, mean = 550, sd = 50) * dnorm(y, mean = 500, sd = 50)) df4 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df4$z <- with(df4, dnorm(x, mean = 650, sd = 50) * dnorm(y, mean = 350, sd = 50)) # 计算所有数据集z值的全局最小值和最大值 min_z <- min(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE) max_z <- max(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE) # 创建具有共同颜色刻度的单个绘图 p1 <- ggplot(df1, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + labs(y = "Excitation Wavelength / nm") + theme(axis.title.x = element_blank()) p2 <- ggplot(df2, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + theme(axis.title = element_blank()) p3 <- ggplot(df3, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + labs(x = "Emission Wavelength / nm", y = "Excitation Wavelength / nm") p4 <- ggplot(df4, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + labs(x = "Emission Wavelength / nm") + theme(axis.title.y = element_blank()) # 组合成2×2网格图,共享轴标题和图例 plot_combined <- (p1 + p2) / (p3 + p4) + plot_layout(axis_titles = "collect", guides = "collect") + plot_annotation( title = "Emission-Excitation-Matrix", subtitle = "Rayleigh Filtered Data" ) # 显示绘图 print(plot_combined)
生成的图表:
解决方案
问题出在你重复在多个子图中设置了相同的轴标题,patchwork的axis_titles = "collect"会收集所有存在的轴标题,导致重复显示。要实现全局共享轴标题,有两种可行方法:
方法1:仅在对应位置的子图设置轴标题
只在最左侧的子图设置Y轴标题,只在最底部的子图设置X轴标题,其他子图完全隐藏轴标题,patchwork会自动将这些标题作为全局标题展示:
修改后的代码:
library(ggplot2) library(patchwork) # 数据集部分保持不变 df1 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df1$z <- with(df1, dnorm(x, mean = 500, sd = 50) * dnorm(y, mean = 400, sd = 50)) df2 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df2$z <- with(df2, dnorm(x, mean = 600, sd = 50) * dnorm(y, mean = 450, sd = 50)) df3 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df3$z <- with(df3, dnorm(x, mean = 550, sd = 50) * dnorm(y, mean = 500, sd = 50)) df4 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df4$z <- with(df4, dnorm(x, mean = 650, sd = 50) * dnorm(y, mean = 350, sd = 50)) min_z <- min(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE) max_z <- max(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE) # 修改子图定义:仅在p1设置Y轴标题,仅在p3设置X轴标题,其余子图隐藏所有轴标题 p1 <- ggplot(df1, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + labs(y = "Excitation Wavelength / nm") + theme(axis.title.x = element_blank()) p2 <- ggplot(df2, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + theme(axis.title = element_blank()) p3 <- ggplot(df3, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + labs(x = "Emission Wavelength / nm") + theme(axis.title.y = element_blank()) # 去掉p3的Y轴标题 p4 <- ggplot(df4, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + theme(axis.title = element_blank()) # 去掉p4的X轴标题 # 组合时保留plot_layout的collect参数 plot_combined <- (p1 + p2) / (p3 + p4) + plot_layout(axis_titles = "collect", guides = "collect") + plot_annotation( title = "Emission-Excitation-Matrix", subtitle = "Rayleigh Filtered Data" ) print(plot_combined)
方法2:通过plot_annotation添加全局轴标题
更简洁的方式是让所有子图都隐藏轴标题,直接在plot_annotation环节添加全局的X/Y轴标题,配合主题调整标题位置:
修改后的代码:
library(ggplot2) library(patchwork) # 数据集部分保持不变 df1 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df1$z <- with(df1, dnorm(x, mean = 500, sd = 50) * dnorm(y, mean = 400, sd = 50)) df2 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df2$z <- with(df2, dnorm(x, mean = 600, sd = 50) * dnorm(y, mean = 450, sd = 50)) df3 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df3$z <- with(df3, dnorm(x, mean = 550, sd = 50) * dnorm(y, mean = 500, sd = 50)) df4 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50)) df4$z <- with(df4, dnorm(x, mean = 650, sd = 50) * dnorm(y, mean = 350, sd = 50)) min_z <- min(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE) max_z <- max(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE) # 所有子图都隐藏轴标题 p1 <- ggplot(df1, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + theme(axis.title = element_blank()) p2 <- ggplot(df2, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + theme(axis.title = element_blank()) p3 <- ggplot(df3, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + theme(axis.title = element_blank()) p4 <- ggplot(df4, aes(x, y, fill = z)) + geom_raster() + scale_fill_viridis_c(limits = c(min_z, max_z)) + theme(axis.title = element_blank()) # 组合时通过plot_annotation添加全局轴标题,并调整主题位置 plot_combined <- (p1 + p2) / (p3 + p4) + plot_layout(guides = "collect") + plot_annotation( title = "Emission-Excitation-Matrix", subtitle = "Rayleigh Filtered Data" ) & theme( plot.title = element_text(hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), # 调整全局轴标题的位置与大小 axis.title.x = element_text(vjust = -1, size = 12), axis.title.y = element_text(angle = 90, vjust = 3, size = 12) ) + # 手动设置轴标题文本 labs(x = "Emission Wavelength / nm", y = "Excitation Wavelength / nm") print(plot_combined)
内容的提问来源于stack exchange,提问作者Excelsior
相关产品推荐
相关产品推荐

