如何用R绘制Alluvial Plot(桑基图)展示类别随时间变化并解决排序配色问题
核心问题修复方案
问题根因
- 因子排序逻辑错误:你使用
levels(db$pre) <- c(...)的写法本质是重命名已有因子水平,而非重新指定排序,当数据中某类频次为0时,因子水平会被自动丢弃,最终顺序和预期不符。 - 颜色映射错位:你仅给
fill映射了pre列,且颜色没有和类别名绑定,导致右侧post时段的分层无法匹配对应类别的颜色。 - 缺失水平自动丢弃:默认配置下,ggplot会自动丢弃频次为0的因子水平,两个时段类别覆盖不一致时就会出现排序混乱。
修正后的完整代码
library(ggplot2) library(ggalluvial) db <- read.table(text = "pre post freq NE NE 0 NE DD 2 NE LC 5 NE NT 2 NE VU 3 NE EN 5 NE CR 1 DD NE 0 DD DD 3 DD LC 37 DD NT 10 DD VU 14 DD EN 3 DD CR 3 LC NE 0 LC DD 0 LC LC 18 LC NT 2 LC VU 1 LC EN 2 LC CR 0 NT NE 0 NT DD 1 NT LC 3 NT NT 8 NT VU 13 NT EN 5 NT CR 1 VU NE 0 VU DD 0 VU LC 1 VU NT 0 VU VU 7 VU EN 8 VU CR 3 EN NE 0 EN DD 0 EN LC 0 EN NT 0 EN VU 0 EN EN 0 EN CR 2 CR NE 0 CR DD 0 CR LC 1 CR NT 0 CR VU 0 CR EN 0 CR CR 2 ", header=T) # 提前定义全量类别顺序,后续所有逻辑都基于这个向量,就算有缺失水平也不会乱序 cat_levels <- c("NE", "DD", "LC", "NT", "VU", "EN", "CR") # 正确设置因子水平,显式指定排序,而非修改原水平名称 db$pre <- factor(db$pre, levels = cat_levels) db$post <- factor(db$post, levels = cat_levels) # 给颜色向量命名,让颜色和类别一一绑定,不会出现映射错位 colors.p <- c( "NE" = "#282828", "DD" = "#7C7C7C", "LC" = "#20AB5F", "NT" = "#3EFF00", "VU" = "#FBFF00", "EN" = "#FFBD00", "CR" = "#FF0C00" ) p <- ggplot(db, aes(y = freq, axis1 = pre, axis2 = post)) + geom_alluvium(aes(fill = pre), show.legend = FALSE) + # stratum的fill映射到当前分层的类别值,配合命名颜色向量,左右同类别自动同色 geom_stratum(aes(fill = after_stat(stratum)), color = "black", alpha = 0.5) + geom_label(stat = "stratum", aes(label = after_stat(stratum))) + scale_x_discrete(limits = c("previous", "current"), expand = c(0.3, 0.01)) + # scale_fill_manual设置drop=FALSE,保留所有定义的水平,哪怕频次为0 scale_fill_manual(values = colors.p, drop = FALSE) + theme_void() + theme( axis.text.x = element_text(size = 15, face = "bold"), legend.position = "none" ) p
效果说明
- 排序完全可控:只要提前把所有需要展示的类别放到
cat_levels向量里,不管两个时段是否覆盖所有水平、有没有频次为0的类别,都会按你指定的顺序排列,不会自动丢弃缺失水平。 - 颜色匹配准确:因为颜色和类别名一一绑定,左右两侧同类别的分层会自动使用相同填充色,不会出现错位。
内容的提问来源于stack exchange,提问作者user1626688
相关产品推荐
相关产品推荐

