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

ggplot2/gridExtra垂直对齐多图后,ggsave设宽度丢失比例的解决方法

问题

将多个ggplot2绘制的图形合并为一张图并控制各元素高度,使用ggplot2、grid及gridExtra编写的代码能生成对齐的柱状图与曲线图,但在ggsave()中设置输出PDF宽度(如width=20)时,图形相对比例丢失,仅第一个图形被拉伸填充打印区域。

解决方法

原因分析

第二个图使用coord_fixed(ratio = 22)强制固定了绘图区域的纵横比,当仅在ggsave中设置宽度时,PDF的高度会被该固定比例约束,导致第一个图被迫拉伸以填充剩余空间,破坏了原本设置的heights = unit(c(1,1), "null")高度比例。

可行方案

方案1:使用grid原生PDF设备保存(推荐)

直接用pdf()设备指定输出宽高,能严格保持arrangeGrob定义的布局比例:

# 替换原ggsave代码,改用grid的pdf设备保存
pdf("~/Downloads/mytest.pdf", width = 20, height = 10) # height根据1:1比例和width调整,确保比例匹配
grid.draw(combined_plot)
dev.off()

方案2:在ggsave中同时指定width和height

根据两个图的高度比例(此处为1:1)以及第二个图的coord_fixed比例,计算并同时设置宽高,确保整体比例匹配:

ggsave("~/Downloads/mytest.pdf", combined_plot, width = 20, height = 10, dpi = 300)
# height值需根据实际图形比例调整,保证两个图高度比例保持1:1

方案3:改用patchwork包简化布局(更简洁)

patchwork对多图布局的比例处理更智能,无需手动调整grob宽度,代码更简洁:

library(patchwork)

# 直接合并图形,设置高度比例
combined_plot <- plot_1 / plot_2 + plot_layout(heights = c(1, 1))

# 保存时指定宽高,比例会自动保持
ggsave("~/Downloads/mytest.pdf", combined_plot, width = 20, height = 10, dpi = 300)

完整修改后的代码(方案1)

library(ggplot2)
library(grid)
library(gridExtra)

# 第一个图的数据集
df_1 <- data.frame(
    data = c(0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.2, 0.2, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.4, 0.2,
             0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.5, 0.1, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.2, 0.2,
             0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.4, 0.2, 0.25, 0.5, 0.1, 0.15, 0.5, 0.5, 0.5, 0.1,
             0.5, 0.1, 0.15, 0.5, 0.5, 0.4),
    base = 1:54,
    value = c(0.9557, 0.4407, 0.141, 3.4229, 3.5197, 4.7386, 6.9897, 0.3958, 4.7268, 1.8085,
              10.5312, 5.9011, 7.9393, 5.7363, 13.8539, 11.2096, 14.8407, 11.4008, 0.5412, 10.3042,
              11.3208, 15.6669, 8.2177, 12.6917, 20.7124, 10.9896, 24.7246, 7.0544, 21.0489, 16.4536,
              7.4753, 28.2106, 32.7284, 29.7557, 23.3567, 29.9186, 30.3813, 15.1307, 20.2323, 34.4262,
              20.8036, 18.7994, 41.7036, 26.828, 10.7628, 28.2335, 4.4982, 3.6528, 33.8571, 11.6208,
              43.54, 26.7859, 22.2176, 11.0588)
)

# 绘制第一个柱状图
plot_1 <- ggplot(df_1, aes(x = base, y = data, fill = value)) +
    geom_bar(stat = 'identity', position = position_dodge(width = 0.9)) +
    scale_fill_distiller(palette = 'YlGnBu') +
    theme(
        axis.text.x = element_text(size = 9, angle = 0, vjust = 0.5),
        axis.text.y = element_text(size = 9),
        legend.position = 'right',
        legend.spacing.x = unit(5, 'pt'),
        legend.text = element_text(margin = margin(0, 0, 0, 0, 'pt'), size = 10),
        legend.key = element_rect(fill = NA),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),plot.margin = margin(0,0,0,0)
    ) +
    guides(
        colour = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
        shape = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
        fill = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5)
    )+
    xlim(0, 55)


# 第二个图的数据集
df_2 <- data.frame(
    x1 = c(4, 12, 1, 2),
    x2 = c(9, 15, 24, 54),
    y1 = c(0, 0, 0, 0),
    y2 = c(0, 0, 0, 0),
    priority = c('high', 'low', 'mid', 'high')
)

# 绘制第二个曲线图
plot_2 <- ggplot(df_2) +
    geom_curve(aes(x = x1, xend = x2, y = y1, yend = y2, color = priority),
               curvature = 1, linewidth = 0.5, ncp = 1000) +
    expand_limits(y = -1) +
    coord_fixed(ratio = 22, ylim = c(-1, 0)) +
    scale_colour_brewer(palette = 'YlGnBu') +
    theme(
        legend.position = 'right',
        legend.spacing.x = unit(5, 'pt'),
        legend.text = element_text(margin = margin(0, 0, 0, 0, 'pt'), size = 10),
        legend.key = element_rect(fill = NA),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),plot.margin = margin(0,0,0,0)
    ) +
    guides(
        colour = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
        shape = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5),
        fill = guide_legend(label.position = 'right', title.position = 'top', title.hjust = 0.5)
    ) +
    xlim(0, 55)


# 整理图形列表
plots <- list(plot_1, plot_2)

# 统一图形边距
plots_adjusted <- lapply(plots, "+", theme(plot.margin = margin(0, 0, 0, 0)))

# 转换为grob对象
grob_plots <- lapply(plots_adjusted, ggplotGrob)

# 对齐所有图形的宽度
max_widths <- do.call(unit.pmax, lapply(grob_plots, function(g) g$widths))
grob_plots <- lapply(grob_plots, function(g) { g$widths <- max_widths; g })

# 合并图形,设置高度比例为1:1
combined_plot <- arrangeGrob(grobs = grob_plots, heights = unit(c(1,1), "null"), padding = unit(0, "pt"))

# 使用grid原生PDF设备保存,保持比例
pdf("~/Downloads/mytest.pdf", width = 20, height = 10)
grid.draw(combined_plot)
dev.off()

内容的提问来源于stack exchange,提问作者steven_opp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 08:32:33