You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在双轴折线图上叠加占比(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:35:26