Python Plotly Sankey图中移除多余节点的技术问询
Python Plotly 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
关键修改说明
- 空值严格过滤:对每个任务列,先判断是否为非空且不是纯空白字符串,再拆分并通过
filter(None, ...)去掉拆分后可能出现的空元素 - 链接生成前置校验:只有当关联的两个任务集合都非空时,才生成对应链接,从根源避免空节点的关联
这样修改后,空字符串不会被当成节点加入Sankey图,你就能得到任务之间直接关联的理想效果了。
备注:内容来源于stack exchange,提问作者chad
相关产品推荐
相关产品推荐

