You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R中桑基图(Sankey plot)节点宽度调整及多分类数据适配技术求助

R中桑基图(Sankey plot)节点宽度调整及多分类数据适配技术求助

嘿,你遇到的这个问题我之前帮别人解决过好几次!核心就是要先把那些用&连起来的多分类拆解开,再精准控制每个阶段的节点权重——比如Stage3要等大,其他阶段按特征数量来,这样才能让桑基图完全符合你的要求。我来一步步给你讲清楚怎么做~

首先咱们得先处理数据,原始数据里的A&B这种合并分类,桑基图工具是识别不了的,必须拆成单独的行,才能正确统计每个分类对应的特征数量。我用tidyverse工具包来做预处理,这是R里处理数据最顺手的一套工具了。

第一步:加载工具包并准备原始数据

先把需要的包和你的原始数据准备好:

library(tidyverse)
library(networkD3) # 做交互式桑基图的常用包,也可以用ggalluvial做静态图

我根据你的描述补全了Stage3和Stage4的内容:

df <- data.frame(Features = c("Feature1", "Feature2", "Feature3", "Feature4", "Feature5"),
                 Stage1 = c("A", "A&B", "B", "B&C", "C"),
                 Stage2 = c("D", "D&E", "F", "F&G", "G"),
                 Stage3 = c("X1", "X2", "X3", "X4", "X5"), # 每个特征对应唯一值,保证Stage3节点等大
                 Stage4 = c("f", "f", "f", "f", "g"))

第二步:拆分多分类字符串

把Stage1和Stage2里用&连接的分类拆成单独的行,这样每个分类就能对应到对应的Feature:

# 拆分Stage1的多分类
df_stage1_split <- df %>%
  separate_rows(Stage1, sep = "&")

# 拆分Stage2的多分类
df_stage2_split <- df %>%
  separate_rows(Stage2, sep = "&")

拆分完之后,你就能看到Feature2对应的Stage1会拆成两行:A和B,这样就能正确统计Stage1里A有2个特征、B有3个特征、C有2个特征了,完全符合你的要求。

第三步:构建桑基图的链接与节点数据

桑基图的核心是**链接(links)和节点(nodes)**数据框,我们需要精准控制每个链接的权重(也就是流量大小),以此来控制节点的大小:

1. 构建各阶段的链接关系

# Stage1 → Stage2:统计每个分类组合的特征数量
links_s1s2 <- df %>%
  separate_rows(Stage1, sep = "&") %>%
  separate_rows(Stage2, sep = "&") %>%
  group_by(Stage1, Stage2) %>%
  summarise(value = n_distinct(Features), .groups = "drop")

# Stage2 → Stage3:为了让Stage3节点等大,每个链接的权重设为1(每个Stage3节点只对应1个Feature)
links_s2s3 <- df %>%
  separate_rows(Stage2, sep = "&") %>%
  group_by(Stage2, Stage3) %>%
  summarise(value = 1, .groups = "drop")

# Stage3 → Stage4:统计每个Stage3到Stage4的特征数量
links_s3s4 <- df %>%
  group_by(Stage3, Stage4) %>%
  summarise(value = n(), .groups = "drop")

2. 合并所有链接并匹配节点ID

networkD3要求节点用数字ID来标识,所以我们要把所有唯一节点提取出来,分配ID:

# 合并所有链接
all_links <- bind_rows(
  links_s1s2 %>% rename(source = Stage1, target = Stage2),
  links_s2s3 %>% rename(source = Stage2, target = Stage3),
  links_s3s4 %>% rename(source = Stage3, target = Stage4)
)

# 提取所有唯一节点并分配ID(networkD3的ID从0开始)
nodes <- data.frame(
  name = unique(c(all_links$source, all_links$target))
) %>%
  mutate(id = row_number() - 1)

# 把链接里的分类名称替换成对应的节点ID
all_links <- all_links %>%
  left_join(nodes, by = c("source" = "name")) %>%
  rename(source_id = id) %>%
  left_join(nodes, by = c("target" = "name")) %>%
  rename(target_id = id) %>%
  select(source_id, target_id, value)

第四步:绘制交互式桑基图

现在可以用networkD3生成桑基图了,还能调整节点宽度等样式:

sankeyNetwork(
  Links = all_links,
  Nodes = nodes,
  Source = "source_id",
  Target = "target_id",
  Value = "value",
  NodeID = "name",
  fontSize = 12,
  nodeWidth = 30, # 这里可以调整节点的宽度(像素值)
  iterations = 0 # 固定节点顺序,避免自动调整导致阶段混乱
)

运行这段代码后,你就能看到完全符合要求的桑基图:

  • Stage1的节点大小:A对应2个特征,B对应3个,C对应2个
  • Stage2的节点大小:D对应2个,E对应1个,F对应2个,G对应2个
  • Stage3的每个节点大小完全一致(因为每个节点的入度都是1)
  • Stage4的f对应4个特征,g对应1个特征

备选方案:用ggalluvial做静态桑基图

如果你需要更灵活的样式控制(比如自定义节点颜色、标签位置),可以用ggalluvial包(基于ggplot2),代码更简洁:

library(ggalluvial)

# 整理成ggalluvial需要的格式
df_alluvial <- df %>%
  separate_rows(Stage1, sep = "&") %>%
  separate_rows(Stage2, sep = "&")

# 绘制桑基图
ggplot(df_alluvial,
       aes(y = 1, axis1 = Stage1, axis2 = Stage2, axis3 = Stage3, axis4 = Stage4)) +
  geom_alluvium(aes(fill = Features), width = 1/12) +
  geom_stratum(width = 1/3) + # 调整节点宽度
  geom_text(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("Stage1", "Stage2", "Stage3", "Stage4"), expand = c(0.05, 0.05)) +
  theme_minimal() +
  guides(fill = "none") # 去掉Feature的图例,按需调整

这里的y = 1就保证了Stage3的每个节点大小一致,因为每个节点对应的权重都是1,非常直观。

关键要点总结

  1. 拆分多分类是核心:必须把A&B这种合并字符串拆成单独的行,否则桑基图会把它当成一个单独的节点,完全不符合你的统计需求
  2. 权重控制节点大小:节点的大小由流入/流出的总权重决定,所以我们通过设置value参数来精准控制——比如Stage3的链接权重设为1,就能让所有节点等大
  3. 工具选择看需求:networkD3适合做交互式桑基图(可以拖拽节点、查看流量),ggalluvial适合做静态、高度定制化的桑基图

备注:内容来源于stack exchange,提问作者olu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 11:52:57