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

ggplot柱状图:将Y轴改为各fill分组内百分比而非总百分比

解决ggplot2柱状图分组百分比与文本标注问题

问题描述

使用以下代码生成柱状图时,Y轴为所有柱子的总百分比(总和约100%),需求改为每个fill分组(不同颜色的柱子组)内各X轴类别的百分比总和为100%,同时需要在每个柱子上方显示对应的百分比文本:

long_ov %>%
  filter(variable == "overall_mother_protection" | variable == "overall_child_protection" |
           variable == "overall_child_future") %>%
  ggplot(aes(factor(value,levels = 1:5,
                    labels = c('Sangat Setuju', 'Agak Setuju', 'Tidak Setuju atau Tidak Setuju', 
                               'Agak Tidak Setuju', 'Sangat Tidak Setuju')), 
             fill = factor(variable, levels = c('overall_mother_protection', 'overall_child_protection',
                                                'overall_child_future'),
                           labels = c('Perlindungan Ibu', 'Perlindungan Anak', 'Masa Depan Bagi Anak-Anak')))) + 
  theme_fivethirtyeight() + scale_fill_manual(values=c("#3C4C56", "#DB5628", "#B79D79")) +
  geom_bar(aes(y = (..count..)/sum(..count..)), position = "dodge") + 
  scale_x_discrete(labels = function(x) str_wrap(x, width = 15),
                   drop = FALSE, na.translate = FALSE) +
  scale_y_continuous(labels=percent) +
  ggtitle("Program Yasera menyebabkan peningkatan...", subtitle = "di antara 386 ibu") +
  mh_theme

解决方案

1. 调整Y轴为分组内百分比

推荐先用dplyr预处理数据,按每个fill分组(variable)统计各X轴类别(value)的百分比,再用geom_col绘图,这种方式更直观且便于后续扩展:

library(dplyr)
library(ggplot2)
library(scales)
library(stringr)

# 预处理数据,计算分组内百分比
plot_data <- long_ov %>%
  filter(variable %in% c("overall_mother_protection", "overall_child_protection", "overall_child_future")) %>%
  mutate(
    value = factor(value, levels = 1:5,
                   labels = c('Sangat Setuju', 'Agak Setuju', 'Tidak Setuju atau Tidak Setuju', 
                              'Agak Tidak Setuju', 'Sangat Tidak Setuju')),
    variable = factor(variable, levels = c('overall_mother_protection', 'overall_child_protection',
                                           'overall_child_future'),
                      labels = c('Perlindungan Ibu', 'Perlindungan Anak', 'Masa Depan Bagi Anak-Anak'))
  ) %>%
  group_by(variable, value) %>%
  summarise(count = n(), .groups = "drop") %>%
  group_by(variable) %>%
  mutate(percent = count / sum(count))

# 绘制柱状图
ggplot(plot_data, aes(x = value, y = percent, fill = variable)) +
  theme_fivethirtyeight() +
  scale_fill_manual(values=c("#3C4C56", "#DB5628", "#B79D79")) +
  geom_col(position = position_dodge(width = 0.9)) +
  scale_x_discrete(labels = function(x) str_wrap(x, width = 15),
                   drop = FALSE, na.translate = FALSE) +
  scale_y_continuous(labels=percent, limits = c(0, 1)) +
  ggtitle("Program Yasera menyebabkan peningkatan...", subtitle = "di antara 386 ibu") +
  mh_theme

如果坚持直接用geom_bar计算,可修改参数按fill分组统计百分比:

geom_bar(aes(y = after_stat(count / sum(count))), 
         position = position_dodge(width = 0.9),
         stat = "count") +

2. 添加柱子上方的百分比文本

基于预处理好的数据,添加geom_text实现文本标注,通过position_dodge对齐柱子位置,调整参数避免文本被截断:

ggplot(plot_data, aes(x = value, y = percent, fill = variable)) +
  theme_fivethirtyeight() +
  scale_fill_manual(values=c("#3C4C56", "#DB5628", "#B79D79")) +
  geom_col(position = position_dodge(width = 0.9)) +
  # 添加百分比文本
  geom_text(aes(label = percent(percent, accuracy = 1)),
            position = position_dodge(width = 0.9),
            vjust = -0.5,
            size = 3.5) +
  scale_x_discrete(labels = function(x) str_wrap(x, width = 15),
                   drop = FALSE, na.translate = FALSE) +
  scale_y_continuous(labels=percent, limits = c(0, 1.1)) +  # 扩展Y轴上限,避免文本被截断
  ggtitle("Program Yasera menyebabkan peningkatan...", subtitle = "di antara 386 ibu") +
  mh_theme
  • accuracy = 1控制百分比保留整数,可按需改为0.1保留一位小数
  • vjust = -0.5调整文本与柱子的距离,数值越小文本位置越靠上

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 20:25:17