如何调整数据使ggalluvial中数据流在节点处精准衔接?
问题根源
你当前的错误在于把t1→t2和t2→t3的数据流拆成了独立的flow组,导致中间节点(如D)被ggalluvial识别为两个不同的分层元素,尺寸叠加成流入+流出的总和,同时数据流无法连贯衔接。桑基图要求每个完整的跨时段路径(t1→t2→t3)对应唯一的alluvium(即flow标识),这样才能保证节点衔接正确、尺寸符合实际流量。
解决方案
1. 构建完整路径数据集
首先要把两段流动数据合并为包含t1-t2-t3完整路径的数据集,同时保证中间节点的流入总量等于流出总量(桑基图的核心逻辑,否则节点尺寸会矛盾)。
针对你的示例数据,先调整data2的value使其与data1中t2的流入量匹配(如果实际业务中中间节点有人员增减,可参考后续的流量不守恒处理方案):
library(dplyr) library(ggalluvial) library(tidyr) # 原始输入数据 data_1 <- tibble(t1 = c("A", "B", "B","C"), t2 = c("D", "E", "F", "G"), value = c(99, 50, 50, 100)) # 调整data2的value,与data1中t2的流入量匹配(保证流量守恒) data_2 <- tibble(t2 = c("D", "E", "F", "G"), t3 = c("H", "H", "I", "J"), value = c(99, 50, 50, 100)) # 合并为t1→t2→t3的完整路径数据,每个路径对应唯一flow data_full <- inner_join(data_1, data_2, by = "t2") %>% mutate(value = pmin(value.x, value.y), # 取流入流出的最小值确保守恒 flow = 1:n()) %>% select(t1, t2, t3, value, flow) # 转换为ggalluvial要求的长格式 data_long <- data_full %>% pivot_longer(cols = c(t1, t2, t3), names_to = "time", values_to = "district")
2. 绘制正确的桑基图
使用完整路径的长格式数据绘图,此时数据流会连贯穿过三个时间段,中间节点尺寸为对应流量的总和:
plot_alluvial <- ggplot(data = data_long, aes(x = time, stratum = district, alluvium = flow, y = value, label = district)) + geom_flow(stat = "alluvium", lode.guidance = "backfront", color = "darkgray") + geom_stratum() + geom_text(stat = "stratum") + theme(legend.position = "bottom") plot_alluvial
3. 处理流量不守恒的场景
如果实际业务中t2节点的流入和流出确实不相等(比如有人员滞留或新增),可以通过添加"增量/减量路径"来保证流量守恒。以你的原始数据为例(D流入99、流出100;E流入50、流出100等):
# 计算t2每个节点的流入流出差值 t2_balance <- data_1 %>% group_by(t2) %>% summarise(in_flow = sum(value)) %>% inner_join(data_2 %>% group_by(t2) %>% summarise(out_flow = sum(value)), by = "t2") %>% mutate(balance = out_flow - in_flow) # 生成增量路径(比如D节点需要补充1单位流量,E节点补充50单位) extra_flow <- t2_balance %>% filter(balance > 0) %>% mutate(t1 = "新增", # 用"新增"标识外部流入的流量 t3 = data_2$t3[match(t2, data_2$t2)], value = balance) %>% select(t1, t2, t3, value) # 合并原始路径和增量路径 data_full_with_extra <- bind_rows( inner_join(data_1, data_2, by = "t2") %>% mutate(value = pmin(value.x, value.y)), extra_flow ) %>% mutate(flow = 1:n()) # 转换长格式并绘图 data_long_extra <- data_full_with_extra %>% pivot_longer(cols = c(t1, t2, t3), names_to = "time", values_to = "district") ggplot(data = data_long_extra, aes(x = time, stratum = district, alluvium = flow, y = value, label = district)) + geom_flow(stat = "alluvium", lode.guidance = "backfront", color = "darkgray") + geom_stratum() + geom_text(stat = "stratum") + theme(legend.position = "bottom")
核心要点
flow必须对应完整的跨时段路径,不能拆分两段独立定义,否则中间节点会被重复识别。- 桑基图要求中间节点的流入总量=流出总量,这是保证节点尺寸和数据流衔接正确的前提,需根据业务逻辑调整数据。
内容的提问来源于stack exchange,提问作者L Smeets
相关产品推荐
相关产品推荐

