使用ggMarginal生成分组边际箱线图时宽度不一致的问题排查
问题:ggMarginal分组边际箱线图宽度不一致的原因与解决
问题场景
用ggMarginal给散点图添加分组边际箱线图时,r和f两组的箱线图宽度始终没法对齐,试过不传递x/yparams、设置固定width、把varwidth设为FALSE(代码如下),结果都一样。
示例代码:
scatterplotMPG = ggplot( data=mpg, aes(x=cty, y = hwy)) + geom_point(data=mpg[mpg$drv%in%c('r', 'f'), ], aes(color=factor(drv)), alpha=0.5) + geom_point(data=mpg[mpg$drv%in%c('4'), ], color='gray', alpha=0.3)+ theme(legend.position = "bottom") ggExtra::ggMarginal(scatterplotMPG, type = "boxplot", groupFill = TRUE, groupColour = TRUE, size=7, xparams = list(varwidth = FALSE), yparams = list(varwidth = FALSE))
效果如下:
问题根源
ggMarginal的参数传递存在局限:你设置的xparams/yparams里的varwidth并没有被正确传递给底层的geom_boxplot,而且它对分组箱线图的宽度控制逻辑是默认根据每组数据情况自动调整,没有做统一对齐处理,所以两组宽度会出现差异。
ggMarginal的可行调整尝试
如果不想换方法,可以试试这两个思路:
- 强制固定箱线图宽度:在
xparams/yparams里同时指定width为固定值,比如xparams = list(varwidth = FALSE, width = 0.7),数值可根据你的图自行调整。 - 修正主图的分组映射:把
drv放到主图的全局aes里,而不是仅在geom_point局部设置,让ggMarginal能正确识别分组并应用统一规则,修改后的主图代码:
scatterplotMPG = ggplot( data=mpg, aes(x=cty, y = hwy, color=factor(drv))) + geom_point(data=mpg[mpg$drv%in%c('r', 'f'), ], alpha=0.5) + geom_point(data=mpg[mpg$drv%in%c('4'), ], color='gray', alpha=0.3)+ theme(legend.position = "bottom")
再调用ggMarginal,此时分组的宽度控制会更准确。
已验证的替代方案:patchwork手动拼接
你已经用patchwork实现了需求,这里把完整流程整理下:
- 先定义边际图的主题,去掉多余元素:
theme_marginal <- theme(axis.title.x = element_blank(), axis.title.y = element_blank(), axis.text.x = element_blank(), axis.text.y = element_blank(), axis.ticks.x = element_blank(), axis.ticks.y = element_blank(), panel.border = element_blank(), plot.margin = margin(1, 1, 1, 1, "pt"), legend.position = "none")
- 单独生成x轴和y轴的边际箱线图(以x轴为例,y轴逻辑类似),这里可以直接控制箱线图的宽度:
xAxisBoxPlot <- ggplot(mpg[mpg$drv%in%c('r', 'f'), ], aes(x=factor(drv), y=cty)) + geom_boxplot(varwidth = FALSE, width = 0.8) + theme_marginal + coord_flip() # x轴边际图需要翻转坐标,和散点图对齐
- 用patchwork拼接所有图:
combinedPointPlot <- wrap_plots( xAxisBoxPlot, plot_spacer(), scatterplotMPG, yAxisBoxPlot, nrow = 2, widths = c(1, 0.05), heights = c(0.05, 1) ) + plot_annotation( title = 'Primary Title', theme = theme(plot.title = element_text(size = 18)))
这种方法完全自主掌控每个图的参数,能确保箱线图宽度一致,灵活性也更高。
内容的提问来源于stack exchange,提问作者Nate Delaney-Busch
相关产品推荐
相关产品推荐

