如何使用ggplot2或patchwork对齐折线图与柱状图的X轴?
折线图与柱状图X轴偏移的解决方法(ggplot2+patchwork)
问题说明
使用ggplot2绘制的折线图(带带状区域)与柱状图通过patchwork拼接后,X轴出现轻微偏移,核心原因是:
- 折线图的X轴为连续型(基于数值型的Site变量)
- 柱状图因
stat_summary(geom="bar")默认将X轴处理为离散型,加上两个图的X轴刻度断点设置不一致,导致坐标系统错位
具体解决方法
方法1:统一X轴属性与刻度(最稳妥)
强制两个图的X轴都为连续型,且使用完全一致的范围和刻度断点,消除偏移根源:
library(ggplot2) library(patchwork) data<-data.frame( Gains=c(NA,18.26,27.7,13.09,-8.36,1.73,5.57,17.1,-31.31,5.43,38.97,18.81,6.12,1.85,NA,16.28,21.18,3.44,-0.14,-3.87,10.57,11.942,-2.12,-0.07,33.34,13.66,14.14,30.66,NA,17.7,24.286,14.638,-7.986,0.622,9.265,3.216,-30.509,4.714,37.78,17.842,11.606,12.188), Site=c(67,66,61,60,58,57,55,52,48,44,42,39,33,24.5,67,66,61,60,58,57,55,52,48,44,42,39,33,24.5,67,66,61,60,58,57,55,52,48,44,42,39,33,24.5), Discharge=c(0,18.26,52.16,65.25,56.89,58.62,64.4,81.5,50.19,55.62,94.59,129.37,146.87,154.17,0,16.28,43.09,46.53,46.39,42.52,53.3,65.242,39.46,39.39,72.73,98.21,112.35,143.01,0,17.7,49.266,63.904,55.918,57.67,67.155,78.255,47.746,52.46,90.24,125.092,141.84,162.68) ) # 提取Site的全局范围,确保两个图X轴范围一致 x_range <- range(data$Site, na.rm = TRUE) # 绘制折线图(统一X轴刻度) a<-ggplot(data, aes(x=Site, y=Discharge))+ stat_summary(geom = "ribbon", fun.min = min, fun.max = max, alpha = 0.25) + stat_summary(geom = "linerange", fun.min = min, fun.max = max, alpha = 0.3) + stat_summary(geom = "line", fun = mean, size=1.2) + geom_point(aes(y = Discharge)) + annotate("rect", xmin = 60, xmax = 57, ymin = -Inf, ymax = Inf, alpha = .08)+ annotate("rect", xmin = 52, xmax = 44, ymin = -Inf, ymax = Inf, alpha = .08)+ scale_y_continuous(n.breaks=10)+ scale_x_reverse(breaks=seq(20,80,5), limits = x_range)+ xlab("River Mile")+ ylab("Discharge (cfs)")+ theme(plot.margin = margin(5,10,5,10)) # 绘制柱状图(指定group=1确保X轴为连续型,统一刻度) b<-ggplot(data, aes(x=Site, y=Gains))+ stat_summary(geom = "bar", fun = mean, aes(group=1))+ annotate("rect", xmin = 60, xmax = 57, ymin = -Inf, ymax = Inf, alpha = .08)+ annotate("rect", xmin = 52, xmax = 44, ymin = -Inf, ymax = Inf, alpha = .08)+ scale_x_reverse(breaks=seq(20,80,5), limits = x_range)+ scale_y_continuous(breaks=seq(-30,50,10))+ ylab("Gain and Loss (cfs)")+ xlab("River Mile")+ theme(plot.margin = margin(5,10,5,10)) # 拼接并强制水平对齐 pwrk <- b / a pwrk + plot_layout(heights = c(1,2)) + plot_annotation(align = "h")
方法2:利用patchwork的对齐参数快速修复
若不想手动统一刻度,可直接使用patchwork的align="h"参数强制水平对齐,但需先确保柱状图的X轴为连续型(添加group=1):
# 仅修改柱状图和拼接部分 b<-ggplot(data, aes(x=Site, y=Gains))+ stat_summary(geom = "bar", fun = mean, aes(group=1))+ # 关键:指定group=1 annotate("rect", xmin = 60, xmax = 57, ymin = -Inf, ymax = Inf, alpha = .08)+ annotate("rect", xmin = 52, xmax = 44, ymin = -Inf, ymax = Inf, alpha = .08)+ scale_x_reverse(breaks=seq(20,80,5))+ scale_y_continuous(breaks=seq(-30,50,10))+ ylab("Gain and Loss (cfs)")+ xlab("River Mile")+ theme(plot.margin = margin(5,10,5,10)) # 拼接时添加align参数 pwrk <- b / a pwrk + plot_layout(heights = c(1,2), align = "h")
方法3:改用分面绘图(共享X轴)
将数据转为长格式,使用facet_wrap实现共享X轴,从根源避免对齐问题:
library(ggplot2) library(tidyr) # 转换为长格式数据 data_long <- data %>% pivot_longer(cols = c(Discharge, Gains), names_to = "Metric", values_to = "Value") ggplot(data_long, aes(x=Site, y=Value))+ # 折线图相关元素 geom_line(data = subset(data_long, Metric=="Discharge"), stat="summary", fun=mean, size=1.2)+ geom_point(data = subset(data_long, Metric=="Discharge"))+ stat_summary(data = subset(data_long, Metric=="Discharge"), geom = "ribbon", fun.min = min, fun.max = max, alpha = 0.25)+ stat_summary(data = subset(data_long, Metric=="Discharge"), geom = "linerange", fun.min = min, fun.max = max, alpha = 0.3)+ # 柱状图相关元素 stat_summary(data = subset(data_long, Metric=="Gains"), geom = "bar", fun = mean, aes(group=1))+ # 添加标注矩形 annotate("rect", xmin = 60, xmax = 57, ymin = -Inf, ymax = Inf, alpha = .08)+ annotate("rect", xmin = 52, xmax = 44, ymin = -Inf, ymax = Inf, alpha = .08)+ # 分面设置,共享X轴,Y轴自由缩放 facet_wrap(~Metric, scales = "free_y", ncol=1, strip.position = "left")+ scale_x_reverse(breaks=seq(20,80,5))+ xlab("River Mile")+ theme(strip.placement = "outside")
内容的提问来源于stack exchange,提问作者DAY
相关产品推荐
相关产品推荐

