Python中如何将DataFrame列中的嵌套键值对解析为新列?
解决Pandas DataFrame中嵌套JSON列的键值对提取问题
你的问题在于嵌套JSON的解析不完整,以及对score2的路径判断有误,我来一步步帮你修正:
首先分析报错原因
你之前的代码报错TypeError: the JSON object must be str, bytes or bytearray, not Series,核心问题有两个:
df['data'].transform(lambda x: json.loads(x))处理后,userMatch对应的依然是JSON字符串(看原始data列的结构,userMatch的值是被引号包裹的转义字符串),你直接用.str.get去访问字典键是行不通的,因为还需要再解析一次内层的JSON。.str.get是用于Series中字符串或字典序列的便捷访问,但这里经过第一次解析后,我们得到的是字典,更适合用apply来操作。
正确的解决方案
我们需要分两步:先完整解析嵌套的JSON,再提取目标score值。
方法一:分步解析+提取
import json import pandas as pd # 模拟你的原始DataFrame df = pd.DataFrame({ 'ID': ['A1', 'A2'], 'data': [ '{"userMatch": "{\"match\":{\"phone\":{\"name\":{\"score\":1}},\"name\":{\"score\":1}}}"}', '{"userMatch": "{\"match\":{\"phone\":{\"name\":{\"score\":0.934}},\"name\":{\"score\":0.952}}}"}' ] }) # 第一步:解析嵌套JSON(外层+内层userMatch的字符串) def parse_nested_json(raw_str): # 先解析外层JSON outer_dict = json.loads(raw_str) # 再解析userMatch对应的内层JSON字符串 inner_dict = json.loads(outer_dict['userMatch']) return inner_dict df['parsed_data'] = df['data'].apply(parse_nested_json) # 第二步:提取score1和score2 # score1的路径是:match → phone → name → score df['score1'] = df['parsed_data'].apply(lambda x: x['match']['phone']['name']['score']) # score2的路径是:match → name → score(你之前的路径错写成了phone.name.name.score) df['score2'] = df['parsed_data'].apply(lambda x: x['match']['name']['score']) # 保留需要的列 final_df = df[['ID', 'score1', 'score2']] print(final_df)
运行后会得到你期望的输出:
ID score1 score2 0 A1 1.000 1.000 1 A2 0.934 0.952
方法二:一步到位(更高效)
如果不想保留中间的parsed_data列,可以直接在一个apply里完成解析和提取:
def extract_scores(raw_str): outer_dict = json.loads(raw_str) inner_dict = json.loads(outer_dict['userMatch']) match_data = inner_dict['match'] # 返回两个score值,自动对应新列 return pd.Series([ match_data['phone']['name']['score'], match_data['name']['score'] ], index=['score1', 'score2']) # 直接将提取的score合并到原DataFrame df[['score1', 'score2']] = df['data'].apply(extract_scores) # 查看结果 print(df[['ID', 'score1', 'score2']])
关键注意点
- 原始
data列的userMatch是嵌套的JSON字符串,必须**两次调用json.loads**才能得到可访问的字典结构。 - score2的正确路径是
match下直接的name键,不是嵌套在phone.name里的,这是你之前的主要误区。
内容的提问来源于stack exchange,提问作者Anubhav Dikshit
相关产品推荐
相关产品推荐

