
本文介绍如何使用队列驱动的广度优先遍历策略,将 Pandas DataFrame 中任意深度嵌套的 `children` 字典列表(如事件树结构)完全扁平化为单层行记录,避免多次调用 `explode()` 的复杂性与不确定性。
在处理树形结构数据(如事件层级、表单嵌套、组织架构)时,原始数据常以嵌套字典列表形式存在,其中 children 字段可递归包含相同结构的子项,且嵌套深度未知。Pandas 的 explode() 方法仅支持单层展开,无法自动处理多级嵌套;若强行链式调用(如 df.explode('children').explode('children')),需预知最大深度,既不健壮也不可维护。
更优雅、通用的解决方案是脱离 DataFrame 操作,在 Python 层完成递归/迭代展开,再统一构建最终 DataFrame。推荐使用 collections.deque 实现广度优先遍历(BFS) —— 它天然适合按层级顺序展开树结构,逻辑清晰、性能稳定、无栈溢出风险(相比深度优先递归)。
以下是完整实现步骤:
✅ 步骤 1:BFS 展开嵌套结构
from collections import deque
import pandas as pd
# 初始化双端队列,放入根节点
queue = deque(d2)
flattened = []
while queue:
node = queue.popleft()
# 保留当前节点所有字段(包括 forms、repetition_id 等)
flattened.append(node)
# 提取并扩展 children:为每个子节点注入 parent_event_id(可选,便于溯源)
children = node.get("children", [])
for child in children:
# 可选:添加父级上下文(强烈建议保留,便于后续分析父子关系)
enriched_child = {**child, "parent_event_id": node["event_id"]}
queue.append(enriched_child)
# 构建最终 DataFrame
df_flat = pd.DataFrame(flattened)✅ 步骤 2:清理冗余字段(关键!)
注意:原始 children 字段在展开后已失去结构意义(每行只代表一个节点),应设为空列表或直接删除,避免混淆:
# 方案 A:清空 children 列(保留列名,值置为 []) df_flat["children"] = df_flat["children"].apply(lambda x: []) # 方案 B:彻底移除 children 列(推荐,除非下游依赖该列存在) df_flat = df_flat.drop(columns=["children"], errors="ignore")
✅ 输出效果验证
运行后,df_flat 将严格匹配预期格式:
- 每行对应树中一个唯一节点(含根、中间、叶子);
- event_id、display_name、form_count、repetition_id 等字段均来自对应节点;
- forms 字段保持原样(列表结构,可后续 explode('forms') 单独展开);
- 若启用 parent_event_id,则可轻松还原层级关系(例如筛选 parent_event_id == 't1' 获取所有直接子节点)。
⚠️ 注意事项
- 不要对 children 列使用 pd.json_normalize() 后拼接:这会导致字段覆盖、索引错乱,且无法处理多层嵌套;
- 避免递归函数:深层嵌套可能触发 RecursionError,BFS 迭代更安全;
- forms 不参与展开逻辑:它属于叶节点属性,应独立处理(如后续 explode('forms') + json_normalize);
- 字段一致性保障:BFS 过程中所有节点均被 **child 解包合并,确保新增字段(如 parent_event_id)与原有字段共存。
该方法时间复杂度为 O(N),N 为树中总节点数,空间复杂度也为 O(N),是处理动态深度嵌套结构的工业级标准解法。










