
本文介绍如何使用广度优先遍历(bfs)递归展开 pandas 中包含多层嵌套 children 字段的字典列表,无需预知嵌套深度,即可将所有层级扁平化为独立行,并保留父子关系。
在实际数据处理中,我们常遇到树形结构的嵌套 JSON 或字典列表(如事件-子事件-表单层级),其中 children 字段本身又是一个字典列表,且嵌套深度未知、不固定。Pandas 的 explode() 方法仅支持单层展开,无法自动递归处理多级嵌套。直接链式调用 explode('children').explode('children') 不可行——既无法预判层数,又会导致字段覆盖与结构错乱。
此时,应跳出 DataFrame 操作思维,在构建 DataFrame 前完成逻辑扁平化。推荐使用广度优先搜索(BFS)配合 collections.deque 实现高效、可控的逐层展开:
from collections import deque
import pandas as pd
# 初始化队列,存入原始顶层节点
queue = deque(d2)
flattened = []
while queue:
node = queue.popleft()
# 保存当前节点(即当前层级的一行数据)
flattened.append(node)
# 提取并扩展其 children:每个 child 补充 parent_event_id(可选但强烈推荐)
for child in node.get("children", []):
# 深拷贝并注入父级上下文(避免修改原数据)
enriched_child = {**child, "parent_event_id": node["event_id"]}
queue.append(enriched_child)
# 构建最终 DataFrame(自动处理嵌套字段,如 forms、children 等保持原样)
df_flat = pd.DataFrame(flattened)✅ 关键优势说明:
- 无需预知深度:BFS 天然适配任意嵌套层级,代码简洁且鲁棒;
- 字段完整性保障:原始字典结构(如 forms 列仍为列表、children 列为空列表或子结构)被完整保留,符合预期输出格式;
- 可扩展性强:通过添加 parent_event_id 等字段,轻松支持后续层级关系分析(如生成路径 t1 → t_01 → t_03);
- 性能友好:deque 的 popleft() 和 append() 均为 O(1),整体时间复杂度为 O(N),N 为总节点数。
⚠️ 注意事项:
- 若原始数据中 children 可能为 None(如示例所示),务必使用 .get("children", []) 防止 TypeError;
- pd.json_normalize() 在此场景不适用——它会将嵌套结构“拍平”为扁平列(如 children.0.form_id),而非生成新行;
- 如需保留原始行索引映射(例如标记某行源自第几个根节点),可在入队时携带元数据,例如 queue.append((node, root_idx))。
最终生成的 df_flat 即为所求:所有事件(含根节点与各级子节点)作为独立行存在,children 列仅保留其直属子节点(若有),forms 列保持原结构,完全匹配预期输出格式。该方法兼顾清晰性、健壮性与工程实用性,是处理动态嵌套结构的首选范式。










