嵌套JSON转pandas扁平化DataFrame报KeyError如何解决
问题根因
报错的核心原因是对pd.json_normalize()的展平逻辑理解有误:
- 该方法默认只会展开嵌套字典类型的字段,遇到值为列表的字段时,会直接把整个列表作为单元格内容保留,不会自动递归展开列表内部的嵌套结构。
- 你使用的DSTC对话数据集是多层嵌套列表结构:最外层是对话列表,每个对话下的
turns是轮次列表,每个轮次下的frames是语义帧列表,每个帧下的actions是动作列表。直接调用pd.json_normalize(jsondata)时,turns列存的还是原始列表对象,根本不会生成你写的turns.frames.actions.act、turns.utterance这类点分列名,自然会触发KeyError。
排查&解决方法
按以下步骤操作即可正确展平:
- 第一步:先验证默认展平的实际列
先运行print(pd.json_normalize(jsondata).columns.tolist()),你会看到输出里只有dialogue_id、services、turns几个列,turns列的类型是object,存的就是没展开的轮次列表,和你预期的列名完全不一致,这就是报错的直接原因。 - 第二步:根据你需要的粒度选择展平方式
json_normalize提供了record_path参数指定要展开的列表路径,meta参数指定要保留的上层字段,多层嵌套建议逐层展开,不容易出错:
如果你需要把每个对话动作拆成单独一行(最细粒度),用以下代码:
import json import pandas as pd with open('./dialogues_001.json', 'r', encoding='utf-8') as f: jsondata = json.load(f) # 第一层:展开对话下的轮次列表,保留对话级字段 turns_df = pd.json_normalize( data=jsondata, record_path='turns', meta=['dialogue_id', 'services'], sep='.' ) # 第二层:展开轮次下的帧列表,保留轮次级字段(比如utterance说话内容) frames_df = pd.json_normalize( data=turns_df.to_dict('records'), record_path='frames', meta=['dialogue_id', 'services', 'utterance', 'turn_id', 'speaker'], sep='.' ) # 第三层:展开帧下的动作列表,拿到act动作标签 final_df = pd.json_normalize( data=frames_df.to_dict('records'), record_path='actions', meta=['dialogue_id', 'services', 'utterance', 'turn_id', 'speaker'], sep='.' ) # 筛选需要的字段即可,此时act是单独列,不会报KeyError target_fields = ['dialogue_id', 'services', 'act', 'utterance'] print(final_df[target_fields])
如果你不需要把每个动作拆成单独行,只想把同轮次的所有动作标签合并成一个列表,展平到轮次级即可,不用拆到最细:
import json import pandas as pd with open('./dialogues_001.json', 'r', encoding='utf-8') as f: jsondata = json.load(f) # 只展开到轮次级 turns_df = pd.json_normalize( data=jsondata, record_path='turns', meta=['dialogue_id', 'services'], sep='.' ) # 提取每个轮次下所有的act标签,合并为列表 turns_df['turn_acts'] = turns_df['frames'].apply( lambda frame_list: [action['act'] for frame in frame_list for action in frame['actions']] ) # 筛选目标字段 target_fields = ['dialogue_id', 'services', 'turn_acts', 'utterance'] print(turns_df[target_fields])
注意事项
- 不要凭直觉手写嵌套字段路径,每次调用
json_normalize后先打印columns确认实际生成的列名,避免路径写错。 - 嵌套字典和嵌套列表的展平逻辑完全不同:字典会自动展开为点分列,列表必须通过
record_path手动指定展开路径,多层列表嵌套不要试图一步完成展平,逐层处理容错率更高。
内容的提问来源于stack exchange,提问作者sariii
相关产品推荐
相关产品推荐

