ggplot2水平条形图双侧堆叠及y轴排序实现问题
水平双向堆叠条形图的ggplot2实现修正方案
需求说明
- 将df1、df3的
age数值置于图表右侧,df2、df4的age数值置于左侧 - y轴的
who类别按df1$age的总和从大到小排序 - df3的
age数值堆叠在df1对应的条形之上,df4的age数值堆叠在df2对应的条形之上
示例数据集
df1 <- data.frame(who = rep(LETTERS[1:5], each = 5), age = 1:25) df2 <- data.frame(who = rep(LETTERS[1:5], each = 5), age = c(rep(1, 5), rep(5, 5), 1, 12, 3, 2,1, 1:5, 6:10)) df3 <- data.frame(who = rep(LETTERS[1:5], each = 4), age = c(rep(2, 10), rep(3, 10))) df4 <- data.frame(who = rep(LETTERS[1:5], each = 5), age = c(rep(1, 5), rep(5, 5), 1, 1, 3, 2, 1, 1:5, 6:10)) myList <- list(df1, df2, df3, df4) names(myList) <- c("df1", "df2", "df3", "df4")
当前代码问题
现有代码的颜色、分组设置逻辑正确,但存在两个核心问题:
- 未正确映射正负值到x轴,导致左侧无条形显示
- 堆叠逻辑错误,df3/df4未正确堆叠在df1/df2之上
当前代码:
p <- dplyr::bind_rows("DF 1" = myList[["df1"]], "DF 2" = myList[["df2"]], "DF 3" = myList[["df3"]], "DF 4" = myList[["df4"]], .id = "ID") %>% count(ID, who, wt = age, name = "age") %>% arrange(match(ID, c("DF 1", "DF 2", "DF 3", "DF 4"))) %>% mutate(who = reorder(who, (ID == "DF 1") * age, sum), value = if_else((ID == "DF 2" | ID == "DF 4"), -age, age) ) %>% ggplot(aes(y = who, x = age, fill = ID, group = 1)) + geom_col(position = "stack", alpha = .6, width = .6) + geom_vline(xintercept = 0) + scale_x_continuous(labels = abs) + scale_y_discrete(expand = c(0,1.4)) + scale_fill_manual(values = c("#74D055FF","#481568FF", "#F1ED6FFF", "#F1ED6FFF"), breaks = c("DF 1", "DF 2", "DF 3", "DF 4")) + theme_bw()
修正后的完整代码
library(dplyr) library(ggplot2) p <- dplyr::bind_rows("DF 1" = myList[["df1"]], "DF 2" = myList[["df2"]], "DF 3" = myList[["df3"]], "DF 4" = myList[["df4"]], .id = "ID") %>% # 按ID和who分组计算age总和 count(ID, who, wt = age, name = "age") %>% # 新增side字段区分左右侧,用于分组堆叠 mutate(side = if_else(ID %in% c("DF 1", "DF 3"), "right", "left"), # 计算正负值,左侧为负,右侧为正 value = if_else(side == "left", -age, age), # 严格按df1的age总和排序who who = reorder(who, -age[ID == "DF 1"], sum)) %>% ggplot(aes(y = who, x = value, fill = ID, group = interaction(who, side))) + # 使用position_stack确保同侧边的条形正确堆叠 geom_col(position = position_stack(reverse = FALSE), alpha = .6, width = .6) + geom_vline(xintercept = 0, linetype = "solid", color = "black") + scale_x_continuous(labels = abs, expand = c(0.05, 0)) + scale_y_discrete(expand = c(0, 0.5)) + scale_fill_manual(values = c("#74D055FF","#481568FF", "#F1ED6FFF", "#F1ED6FFF"), breaks = c("DF 1", "DF 2", "DF 3", "DF 4")) + labs(x = "Age Sum", y = "Who") + theme_bw() print(p)
关键修正点说明
- x轴映射修正:将
x = age改为x = value,利用预先计算的正负值实现左右侧的区分,让左侧的df2/df4条形正确显示在0轴左侧。 - 堆叠分组优化:新增
side字段并设置group = interaction(who, side),确保每个who的左右侧分别独立堆叠,避免跨侧的数值干扰。 - 排序逻辑明确:修改
reorder的参数为-age[ID == "DF 1"],确保who严格按照df1的age总和降序排列。 - 堆叠参数调整:使用
position_stack(reverse = FALSE)确保df3堆叠在df1之上、df4堆叠在df2之上,符合需求的堆叠顺序。
内容的提问来源于stack exchange,提问作者LT17
相关产品推荐
相关产品推荐

