You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 19:54:53