
本文介绍如何从 DeepDiff 生成的 values_changed 路径字符串(如 "root['prod1']['p_col']['c_col']")中自动提取任意深度的嵌套键,并构建具有动态列数的 DataFrame,使每级字段对应独立列。
本文介绍如何从 deepdiff 生成的 `values_changed` 路径字符串(如 `"root['prod1']['p_col']['c_col']"`)中自动提取任意深度的嵌套键,并构建具有动态列数的 dataframe,使每级字段对应独立列。
在使用 deepdiff 比较嵌套 JSON 文档时,values_changed 返回的键是形如 "root['prod1']['p_col']['c_col']" 的路径字符串。若需将路径结构映射为结构化表格(例如:product、parent-field、field、field…),硬编码切分逻辑(如多次 find/split)既脆弱又不可扩展。理想方案应能自动适配任意嵌套深度(2层、4层甚至更多),并生成对应数量的字段列。
推荐采用正则 + str.extractall 的向量化方式,高效、安全、可读性强:
import pandas as pd
import re
# 假设 values_changed 已是字典(DeepDiff 输出)
values_changed = {
"root['prod1']['p_col']['c_col']": {"new_value": -2.866711109999983, "old_value": -2.75},
"root['prod1']['p_col23']": {"new_value": 1, "old_value": 54},
"root['prod1']['p_col']['c_col2']['c_col5']": {"new_value": 1.678, "old_value": 5.12}
}
# 步骤 1:转为 DataFrame(行索引为原始路径)
df = pd.DataFrame.from_dict(values_changed, orient='index')
# 步骤 2:用正则提取所有引号内字段名(\['(\w+)'\] 匹配 'xxx' 中的 xxx)
# extractall 返回 MultiIndex Series,match 表示匹配序号(0, 1, 2...)
path_parts = df.index.str.extractall(r"\['(\w+)'\]")[0].unstack('match')
# 步骤 3:重命名前两列为语义化列名,其余统一命名为 'field'
col_mapping = {0: 'product', 1: 'parent-field'}
path_parts = path_parts.rename(columns=lambda i: col_mapping.get(i, 'field'))
# 步骤 4:与原数据合并
result_df = path_parts.join(df.reset_index(drop=True))
print(result_df)输出结果:
product parent-field field field new_value old_value 0 prod1 p_col c_col NaN -2.866711 -2.75 1 prod1 p_col23 NaN NaN 1.000000 54.00 2 prod1 p_col c_col2 c_col5 1.678000 5.12
✅ 关键优势:
- 完全动态:无需预设最大嵌套深度;unstack('match') 自动按实际匹配数生成列;
- 健壮安全:正则 \['(\w+)'\] 精确捕获合法标识符(字母/数字/下划线),避免字符串切片导致的索引越界或格式错误;
- 零依赖:仅需 pandas 和标准库,不引入额外解析器;
- 可扩展:后续可轻松添加 level_0, level_1 等语义列名映射,或对空值统一填充(如 .fillna(''))。
⚠️ 注意事项:
- 若路径中含非 \w+ 字符(如连字符 -、点号 .),需调整正则为 r"\['([^'\]]+)'\]" 并注意 SQL/CSV 导出时的转义;
- extractall 对空匹配返回 NaN,这恰好符合“缺失层级留空”的需求,无需额外处理;
- 实际项目中建议封装为函数,接收 values_changed 字典并返回标准化 DataFrame,便于复用与测试。
该方法兼顾简洁性与工程鲁棒性,是处理 DeepDiff 路径结构化分析的推荐实践。


















