
本文介绍如何使用 NetworkX 和 pandas 对按 issue 分组的父子关系数据构建有向图,并完整提取每组内的所有简单路径,同时保留原始分组字段 issue,生成结构化层级依赖表。
本文介绍如何使用 networkx 和 pandas 对按 `issue` 分组的父子关系数据构建有向图,并完整提取每组内的所有简单路径,同时保留原始分组字段 `issue`,生成结构化层级依赖表。
在处理具有层级依赖关系的数据(如任务依赖、组织架构、BOM 清单等)时,常需将“父节点→子节点”关系还原为完整的路径链(例如 33 → 34 → 35),并确保每条路径能追溯到其所属的业务上下文(如 issue=1)。原始代码虽能构建全图并提取路径,但未区分不同 issue 组,导致路径混杂、丢失归属信息。正确做法是按 issue 分组建图、独立遍历、合并结果并保留分组键。
以下为完整、健壮的实现方案:
✅ 核心思路
- 使用 df.groupby('issue') 对数据分组,确保每个 issue 构建独立的有向图(避免跨组错误连接);
- 对每组调用自定义函数:构建 nx.DiGraph → 定位根节点(入度为 0)和叶节点(出度为 0)→ 枚举所有简单路径;
- 将每条路径动态展开为字典(键为 Value_Depend_On_ID_0, Value_Depend_On_ID_1, ...),再转为 DataFrame;
- 最后通过 groupby().apply() 自动携带 issue 列,无需手动拼接。
✅ 完整可运行代码
import pandas as pd
import networkx as nx
import numpy as np
# 示例输入数据
df = pd.DataFrame({
'issue': [1, 1, 2],
'father': [33, 34, 33],
'son': [34, 35, 34]
})
def extract_hierarchy_per_issue(group):
"""对单个 issue 组提取所有层级路径"""
if len(group) == 0:
return pd.DataFrame()
# 构建有向图(仅当前 issue 的边)
G = nx.from_pandas_edgelist(
group,
source='father',
target='son',
create_using=nx.DiGraph
)
# 查找根节点(无父节点)和叶节点(无子节点)
roots = [v for v, d in G.in_degree() if d == 0]
leaves = [v for v, d in G.out_degree() if d == 0]
# 提取所有从根到叶的简单路径
all_paths = []
for root in roots:
for leaf in leaves:
paths = nx.all_simple_paths(G, root, leaf)
all_paths.extend(paths)
# 处理孤立节点(无边但存在 father/son 值的情况,按需启用)
# 若需包含单点路径(如只有 33 无后续),可补充:
# isolated = set(G.nodes()) - {n for p in all_paths for n in p}
# all_paths.extend([[n] for n in isolated])
# 转换为扁平化 DataFrame,列名按层级编号
result_rows = []
for path in all_paths:
row = {f'Value_Depend_On_ID_{i}': val for i, val in enumerate(path)}
result_rows.append(row)
return pd.DataFrame(result_rows)
# 按 issue 分组处理,自动保留 issue 列
result = (df
.groupby('issue', group_keys=False)
.apply(extract_hierarchy_per_issue)
.reset_index(drop=True))
# 可选:统一列类型(Int64 支持 NaN)
hierarchy_cols = [c for c in result.columns if c.startswith('Value_Depend_On_ID_')]
result[hierarchy_cols] = result[hierarchy_cols].astype('Int64')
print(result)✅ 输出示例
issue Value_Depend_On_ID_0 Value_Depend_On_ID_1 Value_Depend_On_ID_2 0 1 33 34 <NA> 1 1 33 34 35 2 2 33 34 <NA>
⚠️ 注意:由于 issue=1 含两条边 (33→34) 和 (34→35),故生成两条路径:[33,34] 和 [33,34,35];而 issue=2 仅含 (33→34),因此只有一条长度为 2 的路径。若需仅保留最长路径(即完整链路),可在 all_simple_paths 后添加筛选逻辑:max(all_paths, key=len)。
✅ 关键注意事项
- 图隔离性:务必分组建图,否则 issue=1 和 issue=2 的相同节点(如 33、34)会被错误连通;
- 空组处理:groupby().apply() 中需防御性检查空 group,避免 nx.from_pandas_edgelist 报错;
-
数据类型一致性:使用 astype('Int64') 保持整数列支持缺失值(
),优于 float64 的 NaN; - 性能提示:对大规模层级(深度 > 10 或节点 > 1000),all_simple_paths 可能指数级增长,建议增加 cutoff 参数限制路径长度。
通过该方法,你不仅能准确还原每组的依赖拓扑,还能无缝继承原始业务标识(如 issue),为后续分析、可视化或导出提供结构清晰、语义明确的层级数据表。
立即学习“Python免费学习笔记(深入)”;










