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

R语言桑基图节点无法归组问题求助

控制R中桑基图节点水平位置的方法

默认情况下,networkD3和plotly的桑基图会根据链接流量自动布局节点,容易出现同名称节点不在同一水平组的问题。以下是两种直接控制节点水平位置的可行方案:

方法1:给networkD3的sankeyNetwork添加自定义JS逻辑

networkD3基于D3.js,我们可以通过自定义JavaScript代码,强制指定每个节点的水平层级,让同名称节点归到同一组。

示例代码

library(networkD3)
library(htmlwidgets)

# 原节点数据新增level列,指定每个节点的水平层级(0到4对应BL、M3、M12、M18、M24)
nodes <- data.frame(
  'name' = c("BL", "M3", "M3", "M3", "M3", "M12", "M12", "M12", "M12", "M18", "M18", "M18", "M18", "M24", "M24", "M24", "M24"),
  'group' = as.factor(c("g","dgr","lgr","g","r","dgr","lgr","g","r","dgr","lgr","g","r","dgr","lgr","g","r")),
  'level' = c(0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4)
)

links <- data.frame(
  'source' = c(0, 0, 0, 0, 3, 3, 3, 3, 4, 4, 4, 4, 7, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 12, 12, 12, 12),
  'target' = c(1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 7, 8, 9, 10, 11, 12, 9, 10, 11, 12, 13, 15, 16, 13, 14, 15, 16),
  'value' = c(70, 80, 1504, 843, 14, 13, 695, 317, 12, 44, 87, 271, 5, 10, 360, 183, 6, 12, 69, 219, 7, 186, 108, 2, 5, 46, 138),
  'group' = as.factor(c("dgr_","lgr_","g_","r_","dgr_","lgr_","g_","r_","dgr_","lgr_","g_","r_","dgr_","lgr_","g_","r_","dgr_","lgr_","g_","r_","dgr_","g_","r_","dgr_","lgr_","g_","r_"))
)

my_color <- 'd3.scaleOrdinal().domain(["g","dgr","lgr","r","g_","dgr_","lgr_","r_"]) .range(["green", "darkgrey", "lightgrey","red", "green", "darkgrey", "lightgrey","red"])'

# 生成基础桑基图
sankey_R = sankeyNetwork(
  Links = links, Nodes = nodes,
  Source = "source", Target = "target",
  Value = "value", NodeID = "name",
  fontSize = 12, nodeWidth = 20,
  colourScale = my_color,
  NodeGroup = "group", LinkGroup = "group"
)

# 添加自定义JS,强制固定节点水平位置
sankey_R <- onRender(sankey_R, '
  function(el, x) {
    var nodes = x.nodes;
    var sankey = this.sankey;
    
    // 根据level列设置节点的x轴范围
    nodes.forEach(function(d, i) {
      d.x0 = d.level * (sankey.nodeWidth() + sankey.nodePadding());
      d.x1 = d.x0 + sankey.nodeWidth();
    });
    
    // 更新链接位置并重新绘图
    sankey.relayout();
    sankey.update(el);
  }
')

sankey_R

方法2:使用ggalluvial包手动控制节点分组

ggalluvial基于ggplot2,支持直接指定节点的水平轴分组,能精准控制同名称节点的位置,适合静态可视化场景。

示例代码

library(ggalluvial)
library(ggplot2)

# 转换数据为ggalluvial要求的长格式
flow_data <- data.frame(
  BL = c(rep("g", 1504+843), rep("dgr",70), rep("lgr",80)),
  M3 = c(rep("g",1504), rep("dgr",14), rep("lgr",13), rep("r",317), rep("g",87), rep("dgr",12), rep("lgr",44), rep("r",271)),
  M12 = c(rep("g",695), rep("dgr",14), rep("lgr",13), rep("r",317), rep("g",87), rep("dgr",12), rep("lgr",44), rep("r",271),
          rep("g",360), rep("dgr",5), rep("lgr",10), rep("r",183), rep("g",69), rep("dgr",6), rep("lgr",12), rep("r",219)),
  M18 = c(rep("g",360), rep("dgr",5), rep("lgr",10), rep("r",183), rep("g",69), rep("dgr",6), rep("lgr",12), rep("r",219),
          rep("g",186), rep("dgr",7), rep("r",108), rep("g",46), rep("dgr",2), rep("lgr",5), rep("r",138)),
  M24 = c(rep("g",186), rep("dgr",7), rep("r",108), rep("g",46), rep("dgr",2), rep("lgr",5), rep("r",138))
)

# 绘制桑基图,通过axis参数指定水平分组
ggplot(flow_data, aes(y = 1, axis1 = BL, axis2 = M3, axis3 = M12, axis4 = M18, axis5 = M24)) +
  geom_alluvium(aes(fill = BL), width = 1/12) +
  geom_stratum(width = 1/12, fill = "white", color = "black") +
  geom_text(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("BL", "M3", "M12", "M18", "M24"), expand = c(0.05, 0.05)) +
  scale_fill_manual(values = c("g" = "green", "dgr" = "darkgrey", "lgr" = "lightgrey", "r" = "red")) +
  theme_minimal() +
  theme(axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank())

方案对比

  • 方法1保留了networkD3的交互性,适合需要用户交互的场景。
  • 方法2布局更直观可控,适合生成静态报告或论文插图。

内容的提问来源于stack exchange,提问作者Zsolt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 21:39:51