如何比较两个Pandas DataFrame中的层级结构?(已更新示例数据)
如何比较两个Pandas DataFrame中的层级结构?(已更新示例数据)
嘿,这个问题我之前处理过类似的,这种片段式的父子行确实绕得慌!核心思路就是先把两个DataFrame里的零散层级关系,转换成每个节点的完整根到节点路径,这样就能用Df1的标准路径来直接校验Df2的内容了。咱们一步步来实操:
第一步:处理标准DataFrame(Df1),构建全局层级映射
首先得说明,Df1里的Ultimate Parent列其实是每行片段的顶层,不是全局的终极父,所以咱们直接提取所有直接父子对,不管这个列的干扰:
import pandas as pd # 你的Df1示例数据 df1 = pd.DataFrame({ 'Ultimate Parent': ['A', 'B', 'C'], 'Parent': ['B', 'C', 'D'], 'Child': ['C', 'D', 'E'] }) # 提取所有直接父子关系(包括每行的Ultimate Parent→Parent,Parent→Child) parent_child_pairs = [] for _, row in df1.iterrows(): parent_child_pairs.append( (row['Ultimate Parent'], row['Parent']) ) parent_child_pairs.append( (row['Parent'], row['Child']) ) # 去重后,生成「子节点→直接父节点」的字典 child_to_parent_df1 = {} for parent, child in parent_child_pairs: if child not in child_to_parent_df1: child_to_parent_df1[child] = parent # 找到全局根节点(没有父节点的节点,也就是真正的终极父) all_nodes_df1 = set(child_to_parent_df1.keys()).union(set(child_to_parent_df1.values())) root_df1 = [node for node in all_nodes_df1 if node not in child_to_parent_df1][0] # 这里是'A'
然后写个小函数,给任意节点生成从根到它的完整路径:
def get_full_path(node, child_parent_map, root): path = [node] current = node while current in child_parent_map: current = child_parent_map[current] path.append(current) if current == root: break return ' > '.join(reversed(path)) # 生成Df1所有节点的标准路径 node_path_df1 = {node: get_full_path(node, child_to_parent_df1, root_df1) for node in all_nodes_df1} # 结果: # {'A': 'A', 'B': 'A > B', 'C': 'A > B > C', 'D': 'A > B > C > D', 'E': 'A > B > C > D > E'}
第二步:处理待校验的DataFrame(Df2)
用同样的方法处理Df2,生成它的节点路径:
# 你的Df2示例数据(假设是只有A、D、E的情况) df2 = pd.DataFrame({ 'Ultimate Parent': ['A'], 'Parent': ['D'], 'Child': ['E'] }) # 同样提取父子对 parent_child_pairs_df2 = [] for _, row in df2.iterrows(): parent_child_pairs_df2.append( (row['Ultimate Parent'], row['Parent']) ) parent_child_pairs_df2.append( (row['Parent'], row['Child']) ) # 生成子→父字典 child_to_parent_df2 = {} for parent, child in parent_child_pairs_df2: if child not in child_to_parent_df2: child_to_parent_df2[child] = parent # 找Df2的根节点(这里是'A') all_nodes_df2 = set(child_to_parent_df2.keys()).union(set(child_to_parent_df2.values())) root_df2 = [node for node in all_nodes_df2 if node not in child_to_parent_df2][0] # 生成Df2的节点路径 node_path_df2 = {node: get_full_path(node, child_to_parent_df2, root_df2) for node in all_nodes_df2} # 结果: # {'A': 'A', 'D': 'A > D', 'E': 'A > D > E'}
第三步:用Df1的标准校验Df2
现在就可以直接对比了,分两种情况检查:
- 检查Df2的节点是否在Df1中存在:如果有Df1没有的节点,标记为异常
- 检查Df2的节点路径是否和Df1的标准路径一致:不一致就是层级错误
# 开始校验 validation_results = [] for node, df2_path in node_path_df2.items(): # 情况1:节点不在Df1中 if node not in node_path_df1: validation_results.append(f"⚠️ 节点 {node} 不在标准层级(Df1)中") else: df1_path = node_path_df1[node] # 情况2:路径不一致 if df2_path != df1_path: validation_results.append(f"❌ 节点 {node} 层级错误:Df2路径为「{df2_path}」,标准路径应为「{df1_path}」") # 情况3:路径一致 else: validation_results.append(f"✅ 节点 {node} 层级校验通过:「{df2_path}」") # 输出校验结果 for result in validation_results: print(result)
运行后会输出:
✅ 节点 A 层级校验通过:「A」 ❌ 节点 D 层级错误:Df2路径为「A > D」,标准路径应为「A > B > C > D」 ❌ 节点 E 层级错误:Df2路径为「A > D > E」,标准路径应为「A > B > C > D > E」
额外优化:检查Df2缺失的中间节点
如果还想知道Df2少了哪些Df1里的关键节点,可以直接求差集:
missing_nodes = all_nodes_df1 - all_nodes_df2 if missing_nodes: print(f"\nℹ️ Df2缺失标准层级中的节点:{', '.join(missing_nodes)}") # 输出:ℹ️ Df2缺失标准层级中的节点:B, C
这样就能完整覆盖你的需求啦,把零散的层级片段转换成统一的路径后,对比就变得特别直观,再也不用盯着每行的Ultimate Parent/Child头晕了~
备注:内容来源于stack exchange,提问作者L H
相关产品推荐
相关产品推荐

