如何在ggplot中将指定类别固定为条形图的中间类别?
问题
绘制了一个包含3个分组的条形图,每个分组有3个填充类别,希望将名为Middle的类别始终作为每个条形图的中间类别。数据及初始绘图代码如下:
dat <- data.frame(fill=c("Category A","Category B","Middle", "Category A","Category C","Middle", "Category B","Category C","Middle"), x=c("A","A","A", "B","B","B", "C","C","C"), y=c(.333,.333,.333, .4,.4,.2, .2,.4,.4)) ggplot(dat,aes(x=x,y=y,fill=fill))+ geom_col()
传统的全局因子排序无法实现需求,比如以下设置:
dat$fill <- factor(dat$fill,levels=c("Category A","Middle","Category B","Category C"))
Middle会位于Category A和Category B之间,但无法在Category B和Category C组成的分组中处于中间位置。请问是否有办法将Middle类别始终设置为每个分组的中间类别?
解决方案
要实现每个分组内Middle固定在中间,需要针对每个分组单独定义填充类别的顺序,而非使用全局统一的因子水平。可以借助dplyr的分组处理功能生成分组内的排序变量,再控制条形图的堆叠顺序:
步骤1:生成分组内的排序标记
先按x分组,给每个分组内的填充类别分配排序值:将Middle设为中间值(比如2),剩余两个类别按名称排序分别设为1和3。
library(dplyr) dat <- dat %>% group_by(x) %>% mutate( fill_order = case_when( fill == "Middle" ~ 2, # 对非Middle的类别按名称排序,分配1和3 TRUE ~ as.integer(factor(fill, levels = sort(setdiff(fill, "Middle")))) ) ) %>% ungroup()
步骤2:基于排序变量绘图
在ggplot的aes中添加order = fill_order参数,让条形图按照分组内的自定义顺序堆叠:
ggplot(dat, aes(x = x, y = y, fill = fill, order = fill_order)) + geom_col()
另一种方式:生成分组专属因子
如果希望通过因子水平来控制,也可以为每个分组单独生成因子水平,确保Middle始终在中间:
dat <- dat %>% group_by(x) %>% mutate( fill_grouped = factor( fill, # 每个分组内的顺序为:排序后的非Middle类别[1] → Middle → 排序后的非Middle类别[2] levels = c(sort(setdiff(fill, "Middle"))[1], "Middle", sort(setdiff(fill, "Middle"))[2]) ) ) %>% ungroup() ggplot(dat, aes(x = x, y = y, fill = fill_grouped)) + geom_col() + labs(fill = "fill") # 保持图例名称与原数据一致
这两种方法都能让Middle在每个分组的条形图中固定处于中间位置,解决了全局因子排序无法适配分组动态顺序的问题。
内容的提问来源于stack exchange,提问作者missgwolf
相关产品推荐
相关产品推荐

