Plotly桑基图如何实现连续同节点连接的正常渲染
Plotly桑基图重复同节点交互渲染失败解决方案
问题根源
- 原有代码的节点列表生成逻辑存在缺陷:直接拼接source、target的去重结果,没有做全局去重,当节点同时出现在源和目标列时会生成重复索引,本身就会触发渲染异常。
- 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
相关产品推荐
相关产品推荐

