如何在双轴折线图上叠加占比(0-1)堆叠柱状图?
解决堆叠柱状图+双轴折线图的可视化需求
核心思路
要实现需求,关键是通过线性转换将不同范围的数据映射到同一坐标系中:把柱状图的数值范围转换为左Y轴(line1)的范围,让柱状图填满绘图高度;同时将line2的数值也转换到该范围,再通过双轴刻度转换还原其原始数值。
完整代码实现
1. 加载包与准备数据
library(tidyverse) # 设置随机种子保证结果可重复(实际使用可移除) set.seed(123) df <- data.frame(year = rep(1:5, each = 3), cat = c("small", "med", "large"), count = rep(sample(1:10, 5), each = 3), line1 = rep(sample(30000:40000, 5), each = 3), line2 = rep(sample(200:300, 5), each = 3))
2. 计算转换参数与定义转换函数
# 计算柱状图的最大堆叠高度 max_bar <- df %>% group_by(year) %>% summarise(total = sum(count)) %>% pull(total) %>% max() min_bar <- 0 # line1的数值范围 min_line1 <- min(df$line1) max_line1 <- max(df$line1) # line2的数值范围 min_line2 <- min(df$line2) max_line2 <- max(df$line2) # 柱状图数值转line1范围的函数 bar_to_line1 <- function(x) { (x - min_bar) / (max_bar - min_bar) * (max_line1 - min_line1) + min_line1 } # line2数值转line1范围的函数 line2_to_line1 <- function(x) { (x - min_line2) / (max_line2 - min_line2) * (max_line1 - min_line1) + min_line1 } # line1范围转line2原始数值的逆函数(用于右轴刻度) line1_to_line2 <- function(x) { (x - min_line1) / (max_line1 - min_line1) * (max_line2 - min_line2) + min_line2 } # 提取折线数据(每个年份仅保留一组line1/line2值,减少重复绘图) df_lines <- df %>% distinct(year, line1, line2)
3. 绘制可视化图表
ggplot() + # 堆叠柱状图:转换count值到line1范围,实现占满绘图高度 geom_bar(data = df, aes(x = factor(year), y = bar_to_line1(count), fill = cat), stat = "identity") + # line1折线:直接用原始数值,对应左Y轴 geom_line(data = df_lines, aes(x = factor(year), y = line1, group = 1), color = "red", size = 1) + geom_point(data = df_lines, aes(x = factor(year), y = line1), color = "red", size = 2) + # line2折线:转换到line1范围,后续通过右轴还原原始数值 geom_line(data = df_lines, aes(x = factor(year), y = line2_to_line1(line2), group = 2), color = "blue", size = 1, linetype = "dashed") + geom_point(data = df_lines, aes(x = factor(year), y = line2_to_line1(line2)), color = "blue", size = 2) + # 设置双Y轴:左轴显示line1原始值,右轴通过逆转换显示line2原始值 scale_y_continuous( name = "Line1 数值", limits = c(min_line1, max_line1), sec.axis = sec_axis(trans = ~line1_to_line2(.), name = "Line2 数值") ) + # X轴设置 scale_x_discrete(name = "年份") + # 主题优化 theme_minimal() + theme( legend.title = element_text(text = "类别") )
关键说明
- 柱状图通过
bar_to_line1函数将count值映射到line1的数值范围,因此会填满左Y轴对应的绘图高度,同时左轴直接显示line1的原始标签,无需额外隐藏柱状图的Y轴。 - line2先转换到line1的范围以适配坐标系,再通过
sec_axis的逆转换函数将左轴刻度还原为line2的原始数值,实现双轴显示。 - 提取
df_lines是为了避免重复绘制折线点(原数据每个年份有3条重复的line1/line2记录),提升绘图效率。
注意事项
双轴可视化容易造成数据关联误解,建议仅在必要时使用,并通过颜色、线型等视觉元素明确区分不同系列。
内容的提问来源于stack exchange,提问作者Simeon
相关产品推荐
相关产品推荐

