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

使用patchwork的wrap_plots()组合带unit()尺寸子布局出错的解决方法

解决patchwork组合带unit()固定宽高的子布局时的索引越界错误及图例布局需求

问题描述

使用patchwork包将6个绘图排列为3行2列布局,单独创建每列时用grid::unit()设置固定宽高可正常运行,但组合两个已封装的列时触发错误:

Error: index out of bounds ("unit" subsetting)

初始测试代码

library(ggplot2)
library(patchwork)
library(grid)

# Dummy plots
p_tmp <- lapply(1:6, function(i) {
  ggplot(
    data = mtcars, 
    aes(mpg, wt)
    ) + 
    geom_point() + 
    labs(
      subtitle = paste("Plot", i)
      )
  })

# Individual columns work fine
col1 <- wrap_plots(
  p_tmp[1:3],
  ncol = 1,
  heights = unit(rep(2.5, 3), "cm"),
  widths = unit(2.5, "cm")
) +
  plot_annotation(
    "col1"
  )

col2 <- wrap_plots(
  p_tmp[4:6],
  ncol = 1,
  heights = unit(rep(2.5, 3), "cm"),
  widths = unit(9 / 16 * 2.5, "cm")
) +
  plot_annotation(
    "col2"
  )

# Combining both results in error
wrap_plots(col1, col2, ncol = 2)

补充需求说明

前3个绘图为相同的geom_raster()图,需要共享一个居中显示在第一列下方的填充图例,直接用单次wrap_plots()组合所有绘图无法实现该图例布局,相关测试代码如下:

library(ggplot2)
library(patchwork)

# Create raster plot
df <- expand.grid(x = 1:20, y = 1:20)
df$z <- as.vector(matrix(runif(400), nrow = 20, ncol = 20))

raster_plot <- ggplot(
  data = df, 
  aes(x, y, fill = z)
  ) +
  geom_raster(
    interpolate = FALSE
    ) +
  scale_fill_viridis_c() +
  theme_minimal() +
  theme(
    legend.position = "bottom"
  )

# Define plots
p_tmp <- c(
  rep(list(raster_plot), 3),
  lapply(1:3, function(i) {
    ggplot(
      data = mtcars, 
      aes(mpg, wt)
      ) +  
      geom_point() +
      theme_minimal()
}))

heights <- unit(rep(2, 3), "cm")

# Column 1 – works alone
col1 <- wrap_plots(
  p_tmp[1:3],
  ncol = 1,
  heights = heights,
  widths = unit(2, "cm"),
  guides = "collect"
)

# Column 2 – works alone
col2 <- wrap_plots(
  p_tmp[4:6],
  ncol = 1,
  heights = heights,
  widths = unit(9 / 16 * 2, "cm")
)

# Combining both → error
# wrap_plots(col1, col2, ncol = 2)

# Combining all → no error,但无法实现第一列单独共享图例的布局
wrap_plots(
  p_tmp,
  ncol = 2,
  byrow = FALSE,
  heights = heights,
  widths = unit(c(2, 9 / 16 * 2), rep("cm", 2)),
  guides = "collect",
  axis_titles = "collect"
)

解决方案

方案1:移除子布局的全局尺寸参数,在最外层统一设置

错误根源是嵌套的wrap_plots()重复设置widths参数导致布局参数冲突,将子布局的widths移除,放到最外层的wrap_plots()中统一指定,同时保留子布局的guides="collect"实现第一列图例共享:

# 修改子布局定义,移除widths参数
col1 <- wrap_plots(
  p_tmp[1:3],
  ncol = 1,
  heights = heights,
  guides = "collect"  # 收集第一列的图例到下方
)

col2 <- wrap_plots(
  p_tmp[4:6],
  ncol = 1,
  heights = heights
)

# 最外层组合时设置两列的固定宽度
wrap_plots(col1, col2, ncol = 2, widths = unit(c(2, 9/16*2), "cm"))

方案2:用wrap_elements()封装子布局,单独控制尺寸

如果需要在子布局层面单独控制尺寸,使用wrap_elements()包裹子布局,避免unit参数冲突:

# 用wrap_elements封装子布局,单独设置宽度
col1 <- wrap_elements(
  wrap_plots(
    p_tmp[1:3],
    ncol = 1,
    heights = heights,
    guides = "collect"
  ),
  width = unit(2, "cm")
)

col2 <- wrap_elements(
  wrap_plots(
    p_tmp[4:6],
    ncol = 1,
    heights = heights
  ),
  width = unit(9/16*2, "cm")
)

# 组合两个封装后的布局
wrap_plots(col1, col2, ncol = 2)

两种方案都能解决索引越界错误,同时保留固定尺寸需求,且guides="collect"会将第一列的三个raster图的图例合并显示在第一列下方,满足布局需求。

内容的提问来源于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 11:42:31