Pandas:将含字典列表的列规范化为标签列的方案
高效处理Pandas中嵌套JSON标签的关联与列转换
问题描述
我有一批用作关系型数据库的JSON文件,导入Pandas很简单,但要生成整合多DataFrame数据的表格报告,可通过ID字段实现类SQL关联。但部分键/ID存在于单元格的嵌套JSON对象中——比如某列包含字典列表,每个字典里的tagKeyId关联另一DataFrame的标签键,value是标签值。目标是把这些键值对提取为以标签名命名的列,且每行标签数量可变。
使用pd.json_normalize(deviceDF.tags)会把列表展开成带数字索引的列,无法对应标签名。此前用iterrows循环的方法既过时又低效,还弹出FutureWarning提示需改用iloc,现寻求高效合规的Pandas实现方案。
数据示例
设备数据:
deviceJSON = { "devices": [ { "name": "foo", "noteIds": [], "vendor": "Aruba", "model": "2920F", "tags": [], "id": "<guid>", }, { "location": { "floor": "<guid>", "coord": { "x": 100, "y": 200 } }, "name": "bar", "noteIds": ["32b60e81-8f9d-4ea2-9af0-8e441d31649b"], "vendor": "Cisco", "model": "6500", "tags": [ { "tagKeyId": "68a9a2e4-9d03-42dd-8744-ed0d241f1746", "value": "IDF 1" }, { "tagKeyId": "22a1adc4-bf73-4c52-a3f2-3bfca9d64011", "value": "e-Waste" } ], "id": "<guid>", } ] } deviceDF = pd.DataFrame(deviceJSON['devices'])
标签键数据:
tagKeysJSON = { "tagKeys": [ { "key": "IDF", "id": "68a9a2e4-9d03-42dd-8744-ed0d241f1746", }, { "key": "Disposition", "id": "22a1adc4-bf73-4c52-a3f2-3bfca9d64011", } ] } tagKeysDF = pd.DataFrame(tagKeysJSON['tagKeys'])
解决方案
以下两种方法均为Pandas原生高效操作,避免了iterrows的低效问题:
方法一:字典映射+JSON标准化
适合中小数据量,代码简洁直观:
- 构建
tagKeyId到标签名的映射字典 - 将每行的tags列表转换为{标签名: 值}的字典
- 标准化字典为列,合并回原DataFrame
# 1. 构建tagKeyId到标签名的映射字典 tag_map = tagKeysDF.set_index('id')['key'].to_dict() # 2. 转换每行的tags列表为{标签名: 值}格式 deviceDF['tags_formatted'] = deviceDF['tags'].apply( lambda x: {tag_map.get(item['tagKeyId'], item['tagKeyId']): item['value'] for item in x} ) # 3. 标准化字典为列,并合并回原数据 tags_normalized = pd.json_normalize(deviceDF['tags_formatted']) final_df = pd.concat([deviceDF.drop(['tags', 'tags_formatted'], axis=1), tags_normalized], axis=1)
方法二:Explode+Merge+Pivot
适合大数据量,利用Pandas的向量化操作提升效率:
- 展开tags列,将每个字典单独成行
- 解析字典为列,关联标签名
- 透视转换为标签名列,合并回原数据
# 1. 展开tags列,每个标签字典单独成行 device_exploded = deviceDF.explode('tags').reset_index(drop=True) # 2. 解析tags字典为单独列,并关联标签名 device_tags = pd.json_normalize(device_exploded['tags']) device_tags_merged = device_tags.merge(tagKeysDF, left_on='tagKeyId', right_on='id', how='left') # 3. 透视将标签转为列,合并回原数据 tags_pivoted = device_tags_merged.pivot( index=device_exploded.index, columns='key', values='value' ).reset_index(drop=True) final_df = pd.concat([deviceDF.drop('tags', axis=1), tags_pivoted], axis=1)
最终final_df会将标签名(如IDF、Disposition)作为列,对应行填充标签值,无标签的行显示NaN。
内容的提问来源于stack exchange,提问作者CaNerdIan
相关产品推荐
相关产品推荐

