R & ggplot2:用次Y轴实现100%堆叠柱状图+平均值折线图
解决ggplot2中100%堆叠柱状图+次Y轴平均值折线图的缩放问题
核心思路
100%堆叠柱的Y轴是0-100的百分比区间,而观测平均值的量级通常远大于这个范围,必须先把平均值线性缩放到0-100区间,再通过次Y轴反向转换回原始数值,才能让两个图层都正常显示。
示例数据(模拟你的场景)
set.seed(123) df <- data.frame( category = rep(c("A", "B", "C", "D"), each = 3), group = rep(c("X", "Y", "Z"), 4), value = sample(10:50, 12, replace = TRUE), avg_value = rep(c(120, 90, 150, 110), each = 3) # 每组的观测平均值 )
正确实现代码
library(ggplot2) library(dplyr) # 计算堆叠柱的百分比(自动处理100%堆叠) df <- df %>% group_by(category) %>% mutate(percent = value / sum(value) * 100) %>% ungroup() # 计算缩放因子:把平均值的最大值映射到100(百分比轴的上限) scale_factor <- max(df$avg_value) / 100 # 对平均值做缩放,适配百分比轴范围 df$scaled_avg <- df$avg_value / scale_factor # 绘图 ggplot(df, aes(x = category)) + # 100%堆叠柱状图 geom_col(aes(y = percent, fill = group), position = "fill") + # 叠加缩放后的平均值折线+点 geom_line(aes(y = scaled_avg, group = 1), color = "#ff4d4d", size = 1.2) + geom_point(aes(y = scaled_avg), color = "#ff4d4d", size = 2) + # 设置双Y轴 scale_y_continuous( name = "占比", labels = function(x) paste0(x, "%"), # 次Y轴:把缩放后的值转回到原始平均值 sec.axis = sec_axis( trans = ~ . * scale_factor, name = "观测平均值" ) ) + labs(fill = "分组", x = "类别") + theme_minimal()
关键细节说明
- 100%堆叠柱的实现:用
position = "fill"可以自动把每组的数值转换为百分比,不需要手动计算总和再除法(手动计算也可,效果一致)。 - 缩放逻辑:用平均值的最大值除以100得到缩放因子,确保最大的平均值刚好对应百分比轴的100%位置,避免折线被压缩或超出图表范围。
- 次Y轴转换:通过
sec_axis的trans参数反向计算,把缩放后的数值还原为原始平均值,这样次Y轴显示的就是真实的平均数值。 - Power BI适配:在Power BI的R视觉中使用时,要先在代码开头加载
ggplot2和dplyr包,同时确保数据的分组列(category、group)是文本/因子类型,避免排序混乱。
内容的提问来源于stack exchange,提问作者KRH
相关产品推荐
相关产品推荐

