如何用ggplot创建双y轴并列柱状图?解决刻度差异问题
解决ggplot双y轴并列柱状图的问题
1. 实现并列柱形:数据转长格式
你当前的柱形重叠问题,根源是用两个独立geom_col图层叠加,position="dodge"只对同一图层内的分组生效。正确做法是把宽格式数据转成长格式,让变量名作为分组依据,用单个geom_col实现并列效果。
用tidyr::pivot_longer重塑数据:
library(tidyverse) # 计算各物种的均值(与你原代码逻辑一致) irissum <- iris %>% group_by(Species) %>% summarise(across(everything(), mean)) # 转成长格式:保留Species列,将其余列转为"变量名-数值"的键值对 irissum_long <- irissum %>% pivot_longer(cols = -Species, names_to = "Metric", values_to = "Value") # 绘制仅包含Petal相关变量的并列柱状图 ggplot(irissum_long %>% filter(str_detect(Metric, "Petal")), aes(x = Species, y = Value, fill = Metric)) + geom_col(position = position_dodge(width = 0.8), width = 0.7) + scale_fill_manual(values = c("Petal.Width" = "blue", "Petal.Length" = "red")) + labs(x = "物种", y = "数值", fill = "花瓣指标")
这样每个物种对应的两个柱形就会并列显示,不会重叠。
2. 处理刻度差异极大的双y轴需求
如果真实数据的两个变量刻度差异悬殊,trans参数无法匹配时,需要手动缩放其中一个变量,再对应调整次轴的转换逻辑。比如Petal.Width数值远小于Petal.Length,可以先给Petal.Width乘以一个缩放系数(比如3,根据你的数据范围调整),让它的数值范围与Petal.Length接近,再给次轴设置反向缩放(除以系数)还原真实刻度。
示例代码:
# 定义缩放系数(需根据你的真实数据范围调整) scale_factor <- 3 # 预处理数据:对Petal.Width进行缩放 irissum_adjusted <- irissum %>% mutate(Petal.Width_scaled = Petal.Width * scale_factor) ggplot(irissum_adjusted, aes(x = Species)) + # 绘制缩放后的Petal.Width geom_col(aes(y = Petal.Width_scaled), fill = "blue", position = position_dodge(width = 0.8), width = 0.35) + # 绘制原始的Petal.Length geom_col(aes(y = Petal.Length), fill = "red", position = position_dodge(width = 0.8), width = 0.35) + # 配置主、次y轴 scale_y_continuous( name = "花瓣宽度", # 主轴刻度显示Petal.Width的真实值 breaks = seq(0, max(irissum$Petal.Width), by = 0.5), labels = seq(0, max(irissum$Petal.Width), by = 0.5), expand = c(0, 0), # 次轴反向缩放,显示Petal.Length的真实值 sec.axis = sec_axis( trans = ~ . / scale_factor, name = "花瓣长度", breaks = seq(0, max(irissum$Petal.Length), by = 1), labels = seq(0, max(irissum$Petal.Length), by = 1) ) ) + theme_minimal()
额外建议:优先选择分面图替代双y轴
ggplot官方不推荐使用双y轴,因为容易误导读者。如果业务允许,优先用分面图展示不同刻度的变量,可读性更强:
ggplot(irissum_long %>% filter(str_detect(Metric, "Petal")), aes(x = Species, y = Value, fill = Metric)) + geom_col() + facet_wrap(~Metric, scales = "free_y") + scale_fill_manual(values = c("Petal.Width" = "blue", "Petal.Length" = "red"))
内容的提问来源于stack exchange,提问作者Mark Davies
相关产品推荐
相关产品推荐

