[Python] How do I align the nodes in a Sankey Diagram in Python?

Discussão em 'Python' iniciado por Stack, Agosto 1, 2025.

  1. Stack

    Stack Membro Participativo

    Need some help aligning my Sankey diagram properly.

    I've been working on a Sankey diagram project but can't seem to get the nodes aligned properly, they seem to be a bit off. I've tried to manually set and x/y coordinates, but still not up to what I want. Here is a bit of the generated data I am working with, along with all the script for the diagram.

    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()]

    # x/y coordinates to center Sankey
    x = [
    0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001,
    0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999
    ]

    y = [
    0.05, 0.18, 0.31, 0.44, 0.57, 0.70, 0.83,
    0.05, 0.18, 0.31, 0.44, 0.57, 0.70, 0.83
    ]

    # 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()

    Continue reading...

Compartilhe esta Página