如何调整分组geom_bar条形图中零计数对应的geom_text位置?
解决分组条形图零计数导致文本偏移的问题
问题根源
当分组条形图中存在零计数的类别时,geom_text依赖stat="count"动态计算数值会因为缺失数据行,导致position_dodge无法正确分配每个文本的位置,进而出现非零数值偏移到分组中间的情况。
解决方案:提前预处理数据,补全零计数组合
通过提前计算每个分组的百分比,并补全所有feed1与study的组合(包括零计数的情况),让每个x轴类别下的两个分组都有对应数据点,确保position_dodge能精准对齐文本与柱子。
步骤1:加载依赖包并预处理数据
library(ggplot2) library(dplyr) library(tidyr) # 原数据集代码 vec.feed1.sorted <- c(rep(2,1), rep(3,2), rep(4,7), rep(5,15), rep(6,30), rep(7,40)) set.seed(001) vec.feed1 <- sample(vec.feed1.sorted) df.feedback <- data.frame( study = factor(c(rep(1,62),rep(2,33)), levels=c("1","2")), feed1 = factor(vec.feed1, levels=as.character(c(1:7))) ) # 预处理:计算百分比并补全零计数 df_summary <- df.feedback %>% # 统计每个study和feed1的计数 count(study, feed1) %>% # 补全所有feed1和study的组合,缺失的计数设为0 complete(feed1, study, fill = list(n = 0)) %>% # 计算每个study内的百分比 group_by(study) %>% mutate(percent = n / sum(n)) %>% ungroup() %>% # 匹配原代码的x轴标签(若有自定义标签可替换这里的labels参数) mutate(feed1_label = factor(feed1, levels = 1:7, labels = 1:7))
步骤2:修改绘图代码
使用预处理后的df_summary绘图,直接调用计算好的percent字段,无需依赖after_stat:
ggplot(data = df_summary, aes(x = feed1_label, fill = study)) + geom_bar(aes(y = percent), stat = "identity", position = position_dodge(width = 0.9)) + scale_fill_manual( values = c("grey40", "grey60"), name = "event location", labels = c("university", "cinema")) + # 直接使用计算好的percent设置文本位置和内容,零计数时显示空文本 geom_text(aes(y = percent, label = ifelse(percent > 0, scales::percent(percent, accuracy = 1), "")), position = position_dodge(width = 0.9), vjust = -0.5) + ylab("percent of audience relative to location") + xlab("How did you like this event?") + theme(axis.text.x = element_text(angle = 45, hjust = .9)) + scale_y_continuous(labels = scales::percent, limits = c(0, 0.51)) + scale_x_discrete(drop = FALSE) + theme( panel.border = element_rect(linetype = "solid", colour = "black", linewidth = .5, fill = NA), panel.grid.minor = element_line(colour = "grey93", linewidth = .3), panel.grid.major.y = element_line(colour = "grey93", linewidth = .3), panel.background = element_rect(fill = "grey97") ) + theme(axis.title.x.bottom = element_text(margin = margin(t = .15, unit = "in")))
效果说明
预处理后的数据包含了所有feed1和study的组合,每个x轴类别下的两个分组都有对应的数值(零或非零),position_dodge(width=0.9)能精准将文本定位到对应柱子的正上方,解决了零计数导致的偏移问题。
内容的提问来源于stack exchange,提问作者Hildrun Walter
相关产品推荐
相关产品推荐

