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

Plotly桑基图如何实现连续同节点连接的正常渲染

Plotly桑基图重复同节点交互渲染失败解决方案

问题根源

  1. 原有代码的节点列表生成逻辑存在缺陷:直接拼接source、target的去重结果,没有做全局去重,当节点同时出现在源和目标列时会生成重复索引,本身就会触发渲染异常。
  2. Plotly原生桑基图默认会自动合并所有source-target索引完全一致的链路,对value做求和计算,直接传入重复的同节点对链路时,不会自动生成平行链路,反而会出现无报错但画布空白的问题。

实现思路

要展示按时间顺序的重复同节点连接,不需要修改Plotly底层配置,只需要给每一条独立的重复链路插入透明虚拟中转节点:

  • 所有真实节点保留原始ID和显示样式
  • 每一条重复链路对应一个独立的虚拟节点,虚拟节点的标签和对应目标节点一致,填充色、边框设为全透明,视觉上不可见
  • 把原来直接A→B的单段链路拆成「源节点→虚拟节点→目标节点」的两段链路,保证所有链路的source-target对唯一,不会被自动合并
  • 最终视觉效果就是两条平行的、直接连接A和B的链路,宽度对应各自的value值,完全符合时序交互展示需求。

可运行修正代码

import pandas as pd
import plotly.graph_objs as go
import plotly.offline as pyo

pyo.init_notebook_mode()
%matplotlib inline

# 加载数据集,这里用题目给出的示例数据做演示
sankey_df = pd.DataFrame([
    {"user":1, "date":"01-01-2022", "Source":"A", "Target":"B", "Price":10},
    {"user":1, "date":"02-01-2022", "Source":"A", "Target":"B", "Price":15}
])

# 1. 先收集所有真实业务节点,全局去重生成映射
real_nodes = list(set(sankey_df['Source']).union(set(sankey_df['Target'])))
real_node_total = len(real_nodes)
# 节点标签、颜色列表初始化
node_labels = real_nodes.copy()
node_colors = ["#2c517c"] * real_node_total  # 真实节点用实色填充
real_node_map = {name: idx for idx, name in enumerate(real_nodes)}

source_list = []
target_list = []
value_list = []
link_colors = []

# 2. 遍历每条时序记录,插入虚拟节点拆分链路
for row_idx, row in sankey_df.iterrows():
    s_id = real_node_map[row['Source']]
    t_id = real_node_map[row['Target']]
    # 虚拟节点ID接在真实节点索引之后,保证全局唯一
    virtual_id = real_node_total + row_idx
    # 虚拟节点配置:标签和目标节点一致,颜色全透明、无边框
    node_labels.append(row['Target'])
    node_colors.append("rgba(0,0,0,0)")
    
    # 拆分两段链路
    # 第一段:源节点 -> 虚拟节点
    source_list.append(s_id)
    target_list.append(virtual_id)
    value_list.append(row['Price'])
    link_colors.append("rgba(44, 81, 124, 0.4)") # 链路半透明避免重叠遮挡
    # 第二段:虚拟节点 -> 目标节点
    source_list.append(virtual_id)
    target_list.append(t_id)
    value_list.append(row['Price'])
    link_colors.append("rgba(44, 81, 124, 0.4)")

# 3. 桑基图配置
node_config = dict(
    label=node_labels,
    pad=35,
    thickness=15,
    color=node_colors,
    line=dict(width=0) # 关闭所有节点边框,隐藏虚拟节点轮廓
)
link_config = dict(
    source=source_list,
    target=target_list,
    value=value_list,
    color=link_colors
)

fig = go.Figure(go.Sankey(
    node=node_config,
    link=link_config,
    arrangement="snap" # 节点排布模式选snap,避免虚拟节点错位
))

fig.update_layout(title="时序同节点交互桑基图", font_size=12, width=800, height=400)
fig.show(renderer="colab")

优化提示

  • 如果需要区分不同时间的交互链路,可以给不同时间的link_colors设置不同的半透色值,比如第一次交互用蓝色、第二次用橙色,辨识度更高
  • 单对节点间的平行链路超过3条时,适当调大pad参数值,避免链路重叠
  • 不需要提前对相同源目标的记录做value聚合,否则会丢失单条时序交互的独立信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:00:57