如何用ggplot2在同图绘制以age=0为界的左右分布水平条形图
实现双向分组条形图的解决方案
你想要的这种图叫做双向条形图(Diverging Bar Chart),核心思路是调整其中一组数据的数值方向,让两组条形分别出现在0轴的两侧,同时基于指定数据框的排序规则来排列类别。下面是完整的实现步骤:
1. 数据预处理
首先我们需要合并两个数据框,给它们打上分组标签,同时把df2的age值取负数(这样它的条形会出现在0轴的另一侧),最后按照df1的age规则对name进行排序:
library(tidyverse) # 你的原始数据 df1 <- data.frame(name = rep(LETTERS[1:5], each = 5), age = 1:25) df2 <- data.frame(name = rep(LETTERS[1:5], each = 5), age = c(rep(1,5), rep(5,5), 1,12,3,2,1,1:5,6:10)) # 合并并处理数据 combined_df <- bind_rows( df1 %>% mutate(group = "df1"), df2 %>% mutate(group = "df2", age = -age) # 将df2的age取负,放到0轴另一侧 ) %>% # 基于df1的age排序name(默认用中位数,你也可以改成总和/均值) mutate(name = fct_reorder(name, ifelse(group == "df1", age, -age)))
如果希望基于df1中每个name的age总和排序,可以用下面的替代代码,排序逻辑更清晰:
# 先计算df1每个name的age总和 df1_total <- df1 %>% group_by(name) %>% summarise(total_age = sum(age)) # 合并后按总和排序 combined_df <- bind_rows( df1 %>% mutate(group = "df1"), df2 %>% mutate(group = "df2", age = -age) ) %>% left_join(df1_total, by = "name") %>% mutate(name = fct_reorder(name, total_age)) %>% select(-total_age)
2. 绘制双向条形图
接下来用ggplot绘制图形,同时调整坐标轴和样式,让结果更直观:
ggplot(combined_df, aes(x = name, y = age, fill = group)) + geom_bar(stat = "identity", alpha = .6, width = .6) + # 添加0轴参考线,明确分界 geom_vline(xintercept = 0, linetype = "dashed", color = "gray50") + coord_flip() + # 设置你需要的颜色:绿色对应df1,紫色对应df2 scale_fill_manual(values = c("df1" = "#74D055FF", "df2" = "#481568FF")) + # 把y轴的负数标签转为绝对值,避免用户困惑 scale_y_continuous( labels = function(x) abs(x), name = "Age" ) + labs(fill = "Data Source") + theme_bw()
关键逻辑说明
- 把
df2的age取负是核心操作,这样它的条形会自动出现在0轴的另一侧(coord_flip后对应图的左侧) fct_reorder的参数确保排序完全基于df1的age分布,和你单独绘制df1时的排序一致- 调整y轴标签为绝对值,让用户看到的是实际年龄数值,而不是处理后的负数
内容的提问来源于stack exchange,提问作者LT17
相关产品推荐
相关产品推荐

