You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ggplot2分组条形图中显示组内占比而非整体占比?

问题:ggplot2条形图如何将整体占比改为分组内占比?

我正在使用ggplot2绘制条形图,对比数据集三个子组中二分类变量(两个水平)的分布情况。当前图表显示的是占整个数据集的百分比,我希望改为显示每个分组内的占比,请问该如何修改代码?

我的代码如下:

# data
structure(list(Attrition = structure(c(1L, 2L, 1L, 1L, 2L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 2L, 1L, 1L, 
1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 2L, 1L, 2L, 
2L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 2L, 
1L, 1L, 2L, 1L, 1L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 2L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 2L, 
1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L), levels = c("No", 
"Yes"), class = "factor"), Datasets = c("Training", "Training", 
"Original", "Training", "Original", "Training", "Training", "Original", 
"Training", "Training", "Testing", "Original", "Original", "Training", 
"Original", "Original", "Original", "Training", "Training", "Original", 
"Original", "Original", "Original", "Training", "Training", "Original", 
"Original", "Original", "Training", "Original", "Original", "Original", 
"Training", "Original", "Testing", "Training", "Testing", "Testing", 
"Training", "Training", "Training", "Training", "Training", "Original", 
"Original", "Original", "Testing", "Original", "Original", "Training", 
"Testing", "Original", "Original", "Original", "Original", "Testing", 
"Original", "Original", "Original", "Training", "Testing", "Original", 
"Training", "Original", "Original", "Training", "Testing", "Original", 
"Testing", "Original", "Original", "Testing", "Testing", "Original", 
"Training", "Original", "Training", "Testing", "Original", "Original", 
"Original", "Testing", "Original", "Training", "Training", "Original", 
"Training", "Original", "Training", "Original", "Original", "Training", 
"Original", "Original", "Training", "Training", "Original", "Testing", 
"Training", "Training", "Original", "Original", "Original", "Original", 
"Training", "Original", "Original", "Training", "Original", "Original", 
"Testing", "Original", "Training", "Original", "Original", "Original", 
"Training", "Testing", "Original", "Original", "Testing", "Original", 
"Training", "Training", "Training", "Training", "Testing", "Testing", 
"Training", "Original", "Original", "Original", "Original", "Original", 
"Testing", "Testing", "Original", "Original", "Original", "Testing", 
"Original", "Original", "Original", "Original", "Training", "Training", 
"Training")), row.names = c(1916L, 2345L, 512L, 1804L, 712L, 
1510L, 1485L, 887L, 2292L, 1646L, 2741L, 279L, 233L, 2238L, 97L, 
925L, 832L, 1898L, 1677L, 1256L, 21L, 1390L, 805L, 1755L, 2256L, 
663L, 782L, 300L, 1630L, 68L, 165L, 1237L, 2031L, 834L, 2853L, 
2353L, 2856L, 2813L, 2434L, 1701L, 1693L, 2130L, 2340L, 433L, 
1042L, 350L, 2649L, 278L, 710L, 1666L, 2567L, 45L, 1091L, 1333L, 
632L, 2547L, 1022L, 1120L, 788L, 2376L, 2740L, 474L, 2456L, 667L, 
1130L, 2022L, 2715L, 1011L, 2910L, 1181L, 301L, 2707L, 2779L, 
642L, 2028L, 611L, 1781L, 2721L, 1233L, 19L, 553L, 2643L, 638L, 
1881L, 2264L, 341L, 2091L, 451L, 2220L, 835L, 1360L, 1859L, 342L, 
943L, 1965L, 2293L, 543L, 2782L, 2254L, 2242L, 1403L, 979L, 779L, 
222L, 2425L, 908L, 34L, 1551L, 432L, 1053L, 2687L, 787L, 1682L, 
860L, 567L, 595L, 2273L, 2720L, 1132L, 1288L, 2788L, 231L, 2145L, 
2368L, 1945L, 2225L, 2849L, 2578L, 1956L, 408L, 741L, 871L, 859L, 
927L, 2677L, 2654L, 151L, 1006L, 904L, 2683L, 799L, 1387L, 1019L, 
1457L, 2192L, 1942L, 2062L), class = "data.frame")

# plot
ggplot(data = dist.atr) +
  geom_bar(mapping = aes(x = Datasets, fill = Attrition),
           position = "fill") + 
  geom_text(aes(x = Datasets, fill = Attrition, label = scales::percent((..count..)/sum(..count..))),
            stat = "count",
            position = "fill",
            vjust = 1.5,
            size = 6,
            colour = "white") +
  labs(title = "Distribution of Attrition Variable",
       subtitle = "by dataset split", 
       x = "Datasets",
       y = "Distribution")

我猜测需要修改label = scales::percent((..count..)/sum(..count..))及stat = "count"这部分代码,但不确定具体方法。


解决方案

你的方向没错,核心是把整个数据集的总计数改成每个Datasets分组内的总计数,不需要改动stat = "count",只需要修改geom_text里的label计算逻辑即可。

修改后的完整代码

ggplot(data = dist.atr) +
  geom_bar(mapping = aes(x = Datasets, fill = Attrition),
           position = "fill") + 
  geom_text(aes(x = Datasets, fill = Attrition, 
                label = scales::percent(..count../ave(..count.., x))),
            stat = "count",
            position = position_fill(vjust = 0.5),
            size = 6,
            colour = "white") +
  labs(title = "Distribution of Attrition Variable",
       subtitle = "by dataset split", 
       x = "Datasets",
       y = "Percentage within Dataset")

关键修改说明

  1. 占比计算逻辑:将(..count..)/sum(..count..)替换为..count../ave(..count.., x)。ave(..count.., x)会按x轴的每个分组(即Datasets的每个类别)计算该组内的总计数,这样得到的就是每个分组内的占比。
  2. 文字位置优化:把position = "fill"改成position_fill(vjust = 0.5),让文字在每个条形段的中间显示,比原代码的vjust=1.5更美观。
  3. 标签准确性优化:将y轴标签改为"Percentage within Dataset",更清晰说明是分组内占比。

内容的提问来源于stack exchange,提问作者kindundkegel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:03:08