在ggplot中为线条分配第二Y轴的堆叠分组柱状图问题求助
解决堆叠分组柱状图+第二Y轴线条的显示问题
你的代码里有几个关键问题导致线条无法正确关联第二Y轴并显示,我帮你调整了代码并拆解了核心修改点:
问题根源
- 你的
XAXIS是离散字符串类型,用geom_line时必须指定group参数,否则ggplot不知道哪些点属于同一条线,无法完成连线。 - 第二Y轴设置的缩放规则是
~./40,意味着右侧轴数值是左侧轴的1/40,但线条的原始值(0.4-0.8)直接放在左侧轴的刻度范围里太小,需要反向放大(乘以40)才能匹配左侧轴刻度,同时让右侧轴显示原始比例值。 - 你之前过滤了
CATEGORY2 == "XXX_PY",导致只有PY的线条数据,如果需要同时展示TY和PY的线条,这个过滤需要移除。
修改后的完整代码
library(tidyverse) library(magrittr) # 原始数据 df <- data.frame( DATE = c("2019-01-01", "2019-01-08", "2019-01-15") , XXX_TY_T = c(20, 30, 25) , XXX_TY_C = c(10, 5, 15) , XXX_PY_T = c(15, 20, 20) , XXX_PY_C = c(20, 20, 15) , YYYY_TY_TD = c(0.7, 0.8, 0.75) , YYYY_PY_TD = c(0.4, 0.7, 0.6) ) # 整理长格式数据:用case_when更清晰地分类CATEGORY2 df_long <- df %>% gather(CATEGORY, VALUE, XXX_TY_T:YYYY_PY_TD) %>% mutate( CATEGORY2 = case_when( str_detect(CATEGORY, "XXX_TY") ~ "XXX_TY", str_detect(CATEGORY, "XXX_PY") ~ "XXX_PY", TRUE ~ NA_character_ ), XAXIS = paste0(DATE, " | ", CATEGORY2) ) # 柱状图数据:过滤掉线条相关的类别 df_long_bar <- df_long %>% filter(!(CATEGORY %in% c("YYYY_TY_TD", "YYYY_PY_TD"))) # 线条数据:移除XXX_PY的过滤,保留CATEGORY用于分组 df_long_line <- df_long %>% filter(CATEGORY %in% c("YYYY_TY_TD", "YYYY_PY_TD")) %>% rename(LINE = VALUE) %>% mutate( CATEGORY2 = ifelse(CATEGORY == "YYYY_TY_TD", "XXX_TY", "XXX_PY"), XAXIS = paste0(DATE, " | ", CATEGORY2) ) %>% select(XAXIS, CATEGORY, LINE) # 合并柱状图和线条数据 df_long_plot <- left_join(df_long_bar, df_long_line, by = c("XAXIS")) # 绘图:核心调整线条的y值转换和分组 ggplot(df_long_plot) + geom_bar(aes(x = XAXIS, y = VALUE, fill = CATEGORY), stat = "identity", position = "stack") + # 关键:指定group,同时将LINE乘以40匹配左侧Y轴刻度 geom_line(aes(x = XAXIS, y = LINE * 40, group = CATEGORY, color = CATEGORY), stat = "identity", size = 1) + # 设置双Y轴的标签和转换关系 scale_y_continuous( name = "柱状图数值", sec.axis = sec_axis(~./40, name = "线条比例数值") ) + # 给线条设置区分颜色 scale_color_manual(values = c("YYYY_TY_TD" = "#e74c3c", "YYYY_PY_TD" = "#3498db")) + # 旋转x轴标签避免重叠 theme(axis.text.x = element_text(angle = 45, hjust = 1))
关键调整说明
- 线条分组:通过
group = CATEGORY让ggplot识别出两条独立的线条(TY和PY),解决离散x轴无法连线的问题。 - Y轴数值转换:
LINE * 40把线条的原始比例值放大到左侧Y轴的数值范围,配合sec_axis(~./40)让右侧轴显示原始的比例值(比如0.7*40=28,对应右侧轴的0.7)。 - 数据范围:移除了线条数据的PY过滤,如果你只需要显示PY的线条,重新加上
filter(CATEGORY2 == "XXX_PY")即可,但记得保留group参数和数值转换。 - 美化优化:添加了轴标签、线条颜色区分,旋转x轴标签提升可读性。
内容的提问来源于stack exchange,提问作者Felix Grossmann
相关产品推荐
相关产品推荐

