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

Python Plotly Sankey图中移除多余节点的技术问询

Python Plotly Sankey图中移除多余节点的技术问询

问题描述

我现在想在任务之间建立直接的关联关系来生成Sankey图,但结果里有些任务没有直接连接,反而通过一个我圈出来的大空节点中转了——这完全不是我想要的效果。请问怎么把这个多余的节点移除掉?

Sankey图中的多余空节点

我当前的实现代码

def generate_links_and_nodes(dataframe):
        cleaned_links = []
        for _, row in dataframe.iterrows():
            q10_tasks = set(row['q10'].split(', '))
            q3_tasks = set(row['q3'].split(', '))
            q11_tasks = set(row['q11'].split(', '))

            # Create links between q10 and q3
            for q10 in q10_tasks:
                for q3 in q3_tasks:
                    if q10 != q3:
                        cleaned_links.append((q10, q3))

            # Create links between q3 and q11
            for q3 in q3_tasks:
                for q11 in q11_tasks:
                    if q3 != q11:
                        cleaned_links.append((q3, q11))

        # DataFrame from links
        links_df = pd.DataFrame(cleaned_links, columns=["source", "target"])

        # Collect unique nodes
        unique_nodes = sorted(set(pd.concat([links_df['source'], links_df['target']])))
        node_indices = {node: i for i, node in enumerate(unique_nodes)}

        # Map sources and targets to node indices
        sources = links_df['source'].map(node_indices).tolist()
        targets = links_df['target'].map(node_indices).tolist()
        values = [1] * len(links_df)  # Default weight of 1 for each link

        return sources, targets, values, unique_nodes

    # Generate the Sankey diagram inputs
    sources, targets, values, nodes = generate_links_and_nodes(df)

    # Create the Sankey diagram
    fig = go.Figure(data=[go.Sankey(
        node=dict(
            pad=25,
            thickness=70,
            line=dict(color="black", width=0.5),
            label=nodes  # Only sub-tasks are shown
        ),
        link=dict(
            source=sources,
            target=targets,
            value=values
        )
    )])

样本数据

这是我从数据库读取的原始数据:

q3
0               T4.2
1   T4.2, T4.3, T4.4
2               T2.3
3               T2.2
4               T6.3
5               T6.3
6               T6.3
7         T4.1, T4.2
8               T1.3
9               T1.2
10              T1.3
11              T1.3
12              T7.3
13              T2.3
14              T2.1
                             q10
0
1
2
3
4                           T6.2
5                           T6.2
6
7   T1.1, T3.1, T3.2, T4.4, T5.1
8
9
10
11
12                          T7.1
13        T2.1, T2.2, T2.4, T3.2
14
                       q11
0
1   T1.1, T1.3, T3.1, T3.2
2
3
4
5
6
7   T1.1, T1.3, T3.1, T3.2
8
9
10
11
12                    T7.2
13
14

专家解答

问题根源很明显:你的代码没有处理空的任务条目!从样本数据能看到,q10、q11列存在大量空值(比如q10的0-3行是空字符串,q11的多数行也是空的)。当前代码会把这些空字符串当成合法的任务节点,最终在Sankey图里生成那个扎眼的大空节点。

解决方法非常直接:在拆分任务列表时,先过滤掉所有空值和空字符串,确保只有有效的任务ID被加入节点集合。

修改后的核心代码

import pandas as pd
import plotly.graph_objects as go

def generate_links_and_nodes(dataframe):
    cleaned_links = []
    for _, row in dataframe.iterrows():
        # 处理q10任务:过滤空值与空字符串
        q10_tasks = set()
        if pd.notna(row['q10']) and row['q10'].strip():
            q10_tasks = set(filter(None, row['q10'].split(', ')))
        
        # 处理q3任务:过滤空值与空字符串
        q3_tasks = set()
        if pd.notna(row['q3']) and row['q3'].strip():
            q3_tasks = set(filter(None, row['q3'].split(', ')))
        
        # 处理q11任务:过滤空值与空字符串
        q11_tasks = set()
        if pd.notna(row['q11']) and row['q11'].strip():
            q11_tasks = set(filter(None, row['q11'].split(', ')))

        # 仅当两边都有有效任务时才生成链接
        # 生成q10与q3的关联
        if q10_tasks and q3_tasks:
            for q10 in q10_tasks:
                for q3 in q3_tasks:
                    if q10 != q3:
                        cleaned_links.append((q10, q3))

        # 生成q3与q11的关联
        if q3_tasks and q11_tasks:
            for q3 in q3_tasks:
                for q11 in q11_tasks:
                    if q3 != q11:
                        cleaned_links.append((q3, q11))

    # 后续逻辑保持不变
    links_df = pd.DataFrame(cleaned_links, columns=["source", "target"])
    unique_nodes = sorted(set(pd.concat([links_df['source'], links_df['target']])))
    node_indices = {node: i for i, node in enumerate(unique_nodes)}

    sources = links_df['source'].map(node_indices).tolist()
    targets = links_df['target'].map(node_indices).tolist()
    values = [1] * len(links_df)

    return sources, targets, values, unique_nodes

关键修改说明

  1. 空值严格过滤:对每个任务列,先判断是否为非空且不是纯空白字符串,再拆分并通过filter(None, ...)去掉拆分后可能出现的空元素
  2. 链接生成前置校验:只有当关联的两个任务集合都非空时,才生成对应链接,从根源避免空节点的关联

这样修改后,空字符串不会被当成节点加入Sankey图,你就能得到任务之间直接关联的理想效果了。


备注:内容来源于stack exchange,提问作者chad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 10:50:29