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

如何在Python中对齐Sankey图节点?手动设坐标仍未解决

解决Sankey图节点对齐偏移问题

你的问题出在手动设置的固定y坐标没有考虑节点厚度、pad值以及链接流量对节点布局的影响,导致左右对应节点无法精确对齐。以下是修正方案:

核心修改思路

  1. 计算每个类别节点的总流出流量,基于总流量占比确定节点的垂直中心位置,确保左右相同类别的中心y坐标完全一致
  2. 保留arrangement="fixed"模式,避免Plotly自动调整节点位置

修正后的完整代码

import pandas as pd
import plotly.graph_objects as go
from io import StringIO

# Load data
csv = StringIO("""
from_cat,to_cat,percent
rpf,bp,3.55314197051978
rpf,cc,6.19084561675718
rpf,es,1.21024049650892
rpf,ic,2.46702870442203
rpf,rpf,2.26532195500388
rpf,sc,6.54771140418929
bp,bp,0.977501939487975
bp,cc,0.403413498836307
bp,es,0.108611326609775
bp,ic,4.7944142746315
bp,rpf,0.387897595034911
bp,sc,1.81536074476338
ic,bp,0.124127230411171
ic,cc,0.21722265321955
ic,es,0.0155159038013964
ic,ic,0.170674941815361
ic,rpf,0.0155159038013964
ic,sc,0.294802172226532
cc,bp,1.25678820791311
cc,cc,7.50969743987587
cc,es,9.41815360744763
cc,ic,0.775795190069822
cc,rpf,1.05508145849496
cc,sc,20.8068269976726
cc,sr,0.0465477114041893
sc,bp,0.0155159038013964
sc,cc,0.325833979829325
sc,es,1.92397207137316
sc,rpf,0.0155159038013964
sc,sc,4.43754848719938
sr,bp,0.0620636152055857
sr,cc,1.55159038013964
sr,es,5.10473235065943
sr,ic,0.0155159038013964
sr,rpf,0.0155159038013964
sr,sc,9.71295577967417
sr,sr,0.0775795190069822
es,bp,0.108611326609775
es,cc,0.574088440651668
es,es,1.48952676493406
es,ic,0.0310318076027929
es,rpf,0.0620636152055857
es,sc,2.00155159038014
es,sr,0.0465477114041893
""")
df = pd.read_csv(csv, skipinitialspace=True)

# Define category order
cat_order = [
    "es",
    "sr",
    "sc",
    "cc",
    "ic",
    "bp",
    "rpf"
]

df["from_cat"] = pd.Categorical(df["from_cat"], categories=cat_order, ordered=True)
df["to_cat"] = pd.Categorical(df["to_cat"], categories=cat_order, ordered=True)

# sort for deterministic ordering
df = df.sort_values(["from_cat", "to_cat"]).reset_index(drop=True)

# left/right hierarchies and labels 
left_order = cat_order  
right_order = cat_order  

n_left = len(left_order)
n_right = len(right_order)

labels = [f"{c} (L)" for c in left_order] + [f"{c} (R)" for c in right_order]
label_to_index = {label: i for i, label in enumerate(labels)}

# Map and coerce to int
df["source_idx"] = df["from_cat"].map(lambda c: label_to_index.get(f"{c} (L)", -1))
df["target_idx"] = df["to_cat"].map(lambda c: label_to_index.get(f"{c} (R)", -1))

# Convert to numeric ints explicitly
df["source_idx"] = pd.to_numeric(df["source_idx"], downcast="integer", errors="coerce").fillna(-1).astype(int)
df["target_idx"] = pd.to_numeric(df["target_idx"], downcast="integer", errors="coerce").fillna(-1).astype(int)

# Color definitions
CATEGORY_COLORS = {
    "es":          "#F6C57A",
    "sr":          "#A6D8F0",
    "sc":          "#7BDCB5",
    "cc":          "#FFC20A",
    "ic":          "#88BDE6",
    "bp":          "#F4A582",
    "rpf":         "#DDA0DD",
    "Unknown":     "#D3D3D3"
}

# Node and link colors
node_colors = [CATEGORY_COLORS[c] for c in left_order] + [CATEGORY_COLORS[c] for c in right_order]
link_colors = [node_colors[src] for src in df["source_idx"].tolist()]

# --- 关键修改:基于总流量计算节点y坐标,确保左右对齐 ---
# 计算每个左侧节点的总流出量
node_totals = df.groupby("from_cat")["percent"].sum().reindex(cat_order).values
total_flow = node_totals.sum()

# 计算每个节点的中心y坐标(从顶部到底部排列)
cumulative = 0
node_y = []
for flow in node_totals:
    # 节点中心位置 = 累积流量占比 + 当前流量占比的一半
    center = cumulative / total_flow + (flow / total_flow) / 2
    node_y.append(center)
    cumulative += flow

# 左侧节点y坐标,右侧节点直接复用对应类别的y值
x = [0.001]*n_left + [0.999]*n_right
y = node_y + node_y

# Build Sankey diagram 
fig = go.Figure(go.Sankey(
    arrangement="fixed",
    node=dict(
        pad=40,        
        thickness=25,  
        line=dict(color="black", width=0.5),
        label=labels,
        x=x,
        y=y,
        color=node_colors
    ),
    link=dict(
        source=df["source_idx"].tolist(),
        target=df["target_idx"].tolist(),
        value=df["percent"].tolist(),
        color=link_colors,
        hovertemplate="%{source.label} → %{target.label}<br><b>%{value:.2f}%</b><extra></extra>"
    ),
    valueformat=".2f",
    valuesuffix="%"
))

fig.update_layout(
    title="Flow",
    font_size=12,
    paper_bgcolor="#f7f7f7",
    plot_bgcolor="#f7f7f7",
    margin=dict(l=30, r=30, t=60, b=30),
    width=1000,   
    height=800   
)
fig.show()

关键修改说明

  • 计算每个类别节点的总流出流量,确保节点位置与流量规模匹配,同时保证左右相同类别的中心y坐标完全一致
  • 移除了手动固定的等间隔y值,改用基于流量占比的动态计算,避免pad和节点厚度导致的偏移
  • 保持arrangement="fixed"模式,确保节点位置不会被Plotly自动调整

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 14:15:54