在R中用ggplot调整堆叠图的图例、颜色顺序并添加数据标签
ggplot堆叠图问题解决指南
问题描述
我有一个包含多周期数据的数据集df,结构如下:
| histo_period | City | percent | participants |
|---|---|---|---|
| Jul 20 - Aug 20 | London | 50 | 100 |
| Jul 20 - Aug 20 | New York | 20 | 40 |
| Jul 20 - Aug 20 | Paris | 30 | 60 |
| Sep 22 - Oct 22 | London | 10 | 50 |
| Sep 22 - Oct 22 | New York | 60 | 300 |
| Sep 22 - Oct 22 | Paris | 30 | 150 |
| Nov 21 - Dec 21 | London | 45 | 45 |
| Nov 21 - Dec 21 | New York | 25 | 25 |
| Nov 21 - Dec 21 | Paris | 30 | 30 |
绘制堆叠图时遇到三个核心问题:
- 时间周期未按实际先后顺序展示
- 颜色未匹配指定的
colours_order序列 - 城市的堆叠顺序、图例排序未遵循设定的
city_order(Paris, London, New York),默认按字母排序
同时需要在每个条形顶部标注该周期的总参与者数量。
当前使用的代码:
city_order <- c("Paris", "London", "New York") colours_order <- c("#297D96", "#68C7E6", "#9BBB59") ggplot(df, aes(x=histo_period, y=percent, fill=City)) + geom_bar(position = "fill", stat="identity") + scale_fill_manual(values = setNames(colours_order, city_order)) + labs(x = "Year", y= "Percent", fill ="") + theme_light() + theme(axis.title.y = element_text(angle= 0), legend.position = "top", axis.line = element_line(size = 0, colour = "black"), panel.border = element_blank(), panel.background = element_rect(fill = "white"), panel.grid.major = element_blank(), panel.grid.minor = element_blank()) + scale_y_continuous(expand = c(0,0))
解决方案
1. 修正时间周期排序
histo_period默认是字符型,ggplot会按字母顺序排列。需要先提取周期起始日期并转换为可识别的日期格式,再将histo_period转为因子并保留时间顺序:
# 提取每个周期的起始日期并转为日期格式 df$period_start <- as.Date(paste0(substr(df$histo_period, 1, 5), "01"), format = "%b %y %d") # 按起始日期排序后,将histo_period转为因子 df <- df[order(df$period_start), ] df$histo_period <- factor(df$histo_period, levels = unique(df$histo_period))
2. 固定城市堆叠与图例顺序
将City列转为因子,指定city_order为层级,这样ggplot会严格遵循该顺序进行堆叠和图例展示:
df$City <- factor(df$City, levels = city_order)
3. 确保颜色与城市匹配
之前的scale_fill_manual已经通过setNames绑定了颜色与城市,只要City的因子层级正确,颜色会自动对应设定的colours_order。
4. 添加条形顶部总参与者数量
先计算每个周期的总参与者,再用geom_text在条形顶部(y=1位置,因使用position="fill",y轴为0-1比例)添加数值:
# 计算每个周期的总参与者 total_participants <- aggregate(participants ~ histo_period, df, sum)
完整代码
library(ggplot2) city_order <- c("Paris", "London", "New York") colours_order <- c("#297D96", "#68C7E6", "#9BBB59") # 处理时间排序 df$period_start <- as.Date(paste0(substr(df$histo_period, 1, 5), "01"), format = "%b %y %d") df <- df[order(df$period_start), ] df$histo_period <- factor(df$histo_period, levels = unique(df$histo_period)) # 固定城市顺序 df$City <- factor(df$City, levels = city_order) # 计算每个周期总参与者 total_participants <- aggregate(participants ~ histo_period, df, sum) # 绘制堆叠图 ggplot(df, aes(x=histo_period, y=percent, fill=City)) + geom_bar(position = "fill", stat="identity") + scale_fill_manual(values = setNames(colours_order, city_order)) + # 添加顶部总人数文本,inherit.aes=FALSE避免继承原数据的y映射 geom_text(data = total_participants, aes(x = histo_period, y = 1, label = participants), vjust = -0.5, size = 4, inherit.aes = FALSE) + labs(x = "时间周期", y= "占比", fill ="") + theme_light() + theme(axis.title.y = element_text(angle= 0), legend.position = "top", axis.line = element_line(size = 0, colour = "black"), panel.border = element_blank(), panel.background = element_rect(fill = "white"), panel.grid.major = element_blank(), panel.grid.minor = element_blank()) + scale_y_continuous(expand = c(0,0), limits = c(0, 1.1)) # 扩展y轴范围,避免顶部文本被截断
内容的提问来源于stack exchange,提问作者Angela
相关产品推荐
相关产品推荐

