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

如何在R的networkD3 sankeyNetwork中按组水平对齐节点

程序化实现networkD3桑基图节点按组水平对齐的方案

networkD3的sankeyNetwork函数原生不支持按节点组进行水平对齐,目前仅能在渲染完成后手动调整节点位置,但我们需要找到程序化实现的方法,让同组节点自动处于同一水平线上。

当前实现代码

links <- data.frame(
  source = c(
    19, 0, 20, 20, 13, 16, 16, 21, 7, 22,
    23, 23, 8, 10, 1, 4, 2, 14, 17, 3, 5, 
    9, 11, 24, 15, 25, 18
  ),
  target = c(
    26, 6, 27, 28, 16, 19, 21, 29, 12, 30, 
    31, 32, 10, 14, 4, 9, 5, 17, 20, 5, 8, 
    11, 13, 33, 18, 34, 25
  ),
  n = c(
    2L, 3L, 2L, 17L, 18L, 2L, 16L, 16L, 2L, 
    6L, 2L, 2L, 22L, 22L, 25L, 21L, 2L, 22L, 
    19L, 20L, 22L, 21L, 18L, 2L, 2L, 4L, 2L
  )
)

nodes <- data.frame(
  label = c(
    "a", "b", "c", "d", "e", "f", "g", "h",
    "i", "j", "k", "l", "m", "n", "o", "p", 
    "q", "r", "s", "t", "u", "v", "w", "x", 
    "y", "z", "A", "B", "C", "D", "E", "F", 
    "G", "H","I"
  ),
  group = c(
    "9.1", "9.1", "9.1", "9.1", "9.2", "9.2", 
    "9.2", "10.1", "10.1", "10.1", "10.2", 
    "10.2", "10.2", "11.1", "11.1", "11.1", 
    "11.2", "11.2", "11.2", "12.1", "12.1", 
    "12.1", "12.1", "12.1", "12.1", "12.1", 
    "12.2", "12.2", "12.2", "12.2", "12.2",
    "12.2", "12.2", "12.2", "12.2"
  )
)

node_colors <- '
  d3.scaleOrdinal() 
  .domain(["9.1", "9.2", "10.1", "10.2", "11.1", "11.2", "12.1", "12.2"]) 
  .range(["red", "red", "orange", "orange", "green", "green", "blue", "blue"])
'

networkD3::sankeyNetwork(
  Links = links,
  Nodes = nodes,
  Source = "source",
  Target = "target",
  Value = "n",
  NodeID = "label",
  NodeGroup = "group",
  colourScale = node_colors,
  fontSize = 12
)

效果对比

当前效果

当前桑基图效果

期望效果

目标桑基图效果

程序化实现思路

1. 注入自定义D3代码修改节点位置

利用htmlwidgets的onRender函数,在桑基图渲染完成后,强制同组节点的x坐标统一:

library(htmlwidgets)

# 生成基础桑基图
sankey_plot <- networkD3::sankeyNetwork(
  Links = links,
  Nodes = nodes,
  Source = "source",
  Target = "target",
  Value = "n",
  NodeID = "label",
  NodeGroup = "group",
  colourScale = node_colors,
  fontSize = 12
)

# 添加自定义JS逻辑,强制同组节点水平对齐
sankey_plot <- onRender(
  sankey_plot,
  '
  function(el, x) {
    // 定义每组对应的水平位置(可根据需求调整数值)
    var groupXMap = {
      "9.1": 60,
      "9.2": 160,
      "10.1": 260,
      "10.2": 360,
      "11.1": 460,
      "11.2": 560,
      "12.1": 660,
      "12.2": 760
    };
    var nodeWidth = 20; // 和原生桑基图节点宽度保持一致

    // 更新节点的x坐标
    d3.select(el).selectAll(".node").each(function(d) {
      d.x = groupXMap[d.group];
      d.x0 = d.x;
      d.x1 = d.x + nodeWidth;
    });

    // 更新连线的起止位置
    d3.select(el).selectAll(".link").each(function(d) {
      d.source.x1 = d.source.x + nodeWidth;
      d.target.x0 = d.target.x;
    });

    // 重新渲染节点和连线
    d3.select(el).select(".sankey").selectAll(".node")
      .attr("transform", d => `translate(${d.x0},${d.y0})`);
    
    d3.select(el).select(".sankey").selectAll(".link")
      .attr("d", networkD3.sankeyLinkHorizontal());
  }
  '
)

# 输出修改后的桑基图
sankey_plot

2. 预定义节点x坐标,覆盖自动布局

提前为每个节点分配固定的x坐标,再修改底层布局逻辑跳过自动计算:

  • 根据nodes数据框的group列,为每个节点设置x值(例如按组顺序递增)
  • 通过注入JS代码,让sankey布局直接使用预定义的x坐标,不再自动计算

3. 更换工具实现

如果networkD3的限制难以突破,可以选择其他方案:

  • 直接使用D3.js编写自定义桑基图,完全控制节点布局逻辑
  • 使用ggiraph结合ggplot2生成交互式桑基图,通过x美学映射实现分组水平对齐

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 00:49:01