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
相关产品推荐
相关产品推荐

