如何在ggplot2中绘制含前后时间点数据的叠加分组柱状图
搞定ggplot2叠加分组柱状图的问题
我明白你想要的效果:每个问题(pain/fear)下,展示before时间点不同答案的分组柱状图,同时加上after时间点的黑色计数柱状图进行对比。你的现有代码问题出在数据结构没梳理清晰,以及geom_bar的位置映射没设置对,我来帮你一步步修正:
第一步:优化数据预处理
首先我们把数据整理成更清晰的结构,把原来的question列拆分成时间(before/after)和问题类型(pain/fear),这样后续绘图更方便:
library(dplyr) library(ggplot2) library(tidyr) # 原始数据 df <- data.frame( before_fear=c(1,1,1,2,3), before_pain=c(2,2,1,3,1), after_fear=c(1,3,3,2,3), after_pain=c(1,1,2,3,1) ) # 转换为长格式并整理字段 df_long <- df %>% gather(key = "question", value = "answer_option") %>% group_by(question, answer_option) %>% summarise(n = n(), .groups = "drop") %>% # 按问题和答案计数 mutate( time = factor(ifelse(grepl("before", question), "before", "after"), levels = c("before", "after")), # 设定时间顺序 type = gsub("before_|after_", "", question), # 提取问题类型(pain/fear) answer_option = factor(answer_option) # 转换为因子保证顺序 )
第二步:绘制目标图表
现在我们用两个geom_bar分别绘制before和after的数据,通过position_dodge让它们在同一问题下并列展示,同时给after的数据设置黑色填充:
ggplot() + # 绘制before数据:按答案分组的彩色柱状图 geom_bar( data = filter(df_long, time == "before"), aes(x = type, y = n, fill = answer_option), position = position_dodge(width = 0.7), # 分组柱状的间距 width = 0.7, stat = "identity" ) + # 绘制after数据:黑色柱状图,和before的分组柱状并列 geom_bar( data = filter(df_long, time == "after"), aes(x = type, y = n), position = position_dodge(width = 0.7), # 和before用相同的dodge参数保证对齐 width = 0.3, # 设置更窄的宽度,避免重叠 stat = "identity", fill = "black" ) + # 调整图例和标签 scale_fill_discrete(name = "Before Answer") + labs( x = "Question Type", y = "Count", title = "Before vs After Question Responses" ) + # 手动添加after的图例项(让黑色柱状有对应的图例说明) annotate("rect", xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = -Inf, fill = "black", color = NA) + guides( fill = guide_legend(order = 1), color = guide_legend(title = "After", override.aes = list(fill = "black")) ) + theme_classic()
关键说明
- 我们把x轴设置为问题类型(pain/fear),而不是原始的
before_fear这类字段,这样能让before和after的数据在同一问题下对比,更直观。 - 通过
position_dodge(width = 0.7)让before的分组柱状和after的黑色柱状对齐并列,调整width参数可以控制柱状的宽窄,避免重叠。 - 如果想要让after的柱状叠加在before的柱状上方(而不是并列),只需要把
position_dodge换成position_stack即可。
内容的提问来源于stack exchange,提问作者LLL
相关产品推荐
相关产品推荐

