ggplot2横向分组柱状图:固定图层顺序与按组归一化至100%
解决ggplot2横向分组柱状图的两个问题
你在用R的ggplot2绘制横向分组柱状图时遇到两个问题:
- 分组内的柱子会根据数值大小自动排序,需要固定FTP(蓝色)在前、CTP(红色)在后的显示顺序
- 当前是全局数值归一化,需要改为按每个avenue组将数值归一化至100%
1. 固定分组内柱子的顺序
ggplot默认会按因子水平(或字符字典序)排列分组,要固定FTP在前,只需把name列转为指定了水平顺序的因子,同时可以自定义对应颜色确保符合需求。
2. 按avenue组归一化到100%
之前的代码是对所有avenue的数值全局求和计算百分比,现在要改成每个avenue内部求和后计算占比,用dplyr的分组计算可以高效完成。
修改后的完整代码
library(ggplot2) library(tidyverse) # 保留指定小数位数的函数 specify_decimal <- function(x, k) trimws(format(round(x, k), nsmall = k)) # 示例数据 avenues <- c("Avenue1", "Avenue2", "Avenue3", "Avenue4") flytip_amount <- c(1000, 2000, 1500, 250) collection_amount <- c(5, 15, 10, 2000) df <- data.frame(avenues, flytip_amount, collection_amount) # 数据处理:转成长格式 + 按avenue组计算百分比 df_processed <- df %>% # 转成长格式,直接将原始列映射为FTP/CTP pivot_longer( cols = -avenues, names_to = "name", values_to = "amount", names_transform = list(name = ~case_when( .x == "flytip_amount" ~ "FTP", .x == "collection_amount" ~ "CTP" )) ) %>% # 按每个avenue分组计算 group_by(avenues) %>% mutate( total = sum(amount), value = (amount / total) * 100, value = specify_decimal(value, 1) %>% as.numeric() # 转为数值型方便绘图 ) %>% ungroup() %>% # 强制固定分组顺序:FTP在前,CTP在后 mutate(name = factor(name, levels = c("FTP", "CTP"))) # 绘制图表 ggplot(df_processed, aes(x = avenues, fill = name, y = value)) + geom_col(position = "dodge", width = 0.75) + coord_flip() + # 自定义颜色:FTP蓝色,CTP红色 scale_fill_manual(values = c("FTP" = "#1f77b4", "CTP" = "#ff4b5c")) + labs( title = "Flytipping & Collection %", x = "ward_name", y = "Percentage per ward" ) + # 让文本和柱子对齐,避免重叠 geom_text( aes(label = paste0(value, "%")), position = position_dodge(width = 0.75), vjust = -0.5, size = 5 ) + # 调整y轴范围,防止百分比文本超出图表 ylim(0, 110)
关键修改说明
- 用
pivot_longer直接完成数据格式转换和命名,减少冗余中间数据框 group_by(avenues)+mutate(total = sum(amount))实现按组求和归一化mutate(name = factor(name, levels = c("FTP", "CTP")))强制固定分组显示顺序scale_fill_manual指定颜色,确保FTP和CTP的颜色符合需求position_dodge(width = 0.75)让标签和对应柱子对齐,避免文本重叠
内容的提问来源于stack exchange,提问作者Louis Johnston
相关产品推荐
相关产品推荐

