如何调整ggplot2堆叠柱状图单柱宽度并保持柱间间距相等
问题原因
你直接给geom_bar传入宽度向量后间距不一致,是因为ggplot默认离散x轴的每个类别中心是等距分布的,柱子宽度变化后,相邻两个柱子的边缘间距就会出现差异。比如宽度为1的柱子中心在3的位置,左右会各占0.5的长度,会挤占和前一个柱子的空间。
解决方法
手动计算每个类别的x轴位置,严格控制相邻柱子的边缘间距相等,不需要安装额外扩展包,修改后的代码如下:
library(ggplot2) library(dplyr) # 原始数据处理逻辑不变 df <- data.frame(var = c("var1", "var1", "var1", "var2", "var2", "var2", "var3", "var3", "var3"), change = c("better", "same", "worse","better", "same", "worse","better", "same", "worse"), freq = sample(100:500, 9, replace = T)) df <- df %>% group_by(change) %>% mutate(per= prop.table(freq) * 100) %>% mutate(across(per, ~ round(., 1))) # ===== 新增位置、宽度计算逻辑 ===== # 自定义参数:窄柱宽度、宽柱宽度、柱子之间的统一间距 w_narrow <- 0.3 w_wide <- 1 unified_gap <- 0.4 # 获取change的因子水平,默认顺序better、same、worse,coord_flip后worse在顶部 change_levels <- levels(as.factor(df$change)) # 给每个水平指定宽度,这里设置最后一个水平(worse)为宽柱,对应顶部位置 width_vec <- setNames(c(w_narrow, w_narrow, w_wide), change_levels) # 计算每个水平的x轴中心位置,保证相邻间距相等 x_pos <- c() current_end <- 0 for (level in change_levels) { w <- width_vec[[level]] x_pos[[level]] <- current_end + w/2 current_end <- current_end + w + unified_gap } # 把位置和宽度映射到原数据 df$x_pos <- x_pos[as.character(df$change)] df$bar_width <- width_vec[as.character(df$change)] # ===== 绘图逻辑修改 ===== fig3 <- ggplot(df, aes(x=x_pos, y=freq, fill=var)) + # 把width映射到数据中对应的宽度 geom_bar(position="fill", stat="identity", alpha = 0.8, aes(width = bar_width)) + scale_y_continuous(labels = scales::percent) + # x轴刻度替换为原change的类别标签 scale_x_continuous(breaks = unname(x_pos), labels = names(x_pos), name = "change") + theme_classic() + theme(legend.position = "right") + guides(fill = guide_legend(reverse = TRUE)) + coord_flip() fig3
效果说明
运行代码后三个柱子的边缘间距完全相等,顶部的宽柱会自然成为视觉焦点,你可以根据需要调整w_narrow、w_wide、unified_gap三个参数适配你的排版需求。
内容的提问来源于stack exchange,提问作者itsapuntis
相关产品推荐
相关产品推荐

