在循环中创建不同长度的字典以附加到列表中。 (json 到 Pandas 数据帧)

Create dictionaries of varying lengths in loop to append to a list. (json to Pandas DataFrame)

我正在 python 进行分析。我在 json 中收到了 post 请求的响应,我想将一些信息提取到 DataFrame 中,以便在进行一些分析之前与另一个数据帧合并。

json 的形式为:

json =     {'type': 'abc',
            'results': [{'users': [{'id': '1',
                                    'score': 0.9},
                                   {'id': '2', 
                                   'score': 0.2}],
                         'num_users': 2,
                         'name': 'john smith'},
                        {'users': [{'id': '3',
                                    'score': 0.7}],
                         'num_users': 1,
                         'name': 'david jones'},
                        {'users': [{'id': '5',
                                    'score': 0.8},
                                   {'id': '6', 
                                    'score': 0.3}],
                         'num_users': 2,
                         'name': 'jane smith'}]}

在上面的示例中,我想提取名称及其相关联的 ID 和分数:

name id_1 score_1 id_2 score_2
John Smith 1 0.9 2 0.2
David Jones 3 0.7
Jane Smith 5 0.8 6 0.3

我最初的计划是在创建数据框之前循环并创建字典列表,但是当我这样做时,我意识到用户数量和分数各不相同,最初认为它应该有 2 个 ID 和分数每个名字。

scores = []
for i in range(0, len(json):
    scores.append({'name':json[i]['name'],
                   'id_1': json[i]['users'][0]['id'],
                   'score_1': json[i]['users'][0]['score'],
                   'id_2': json[i]['users'][1]['id'],
                   'score_2': json[i]['users'][1]['score']})
pd.DataFrame(scores)

如何根据长度创建具有不同长度和键以及不同键名(_1、_2 等)的字典?当变长字典列表传递给 pd.DataFrame 时,这会起作用吗?

谢谢。

如果 data 是你从问题中得到的字典:

df = pd.DataFrame(
    [
        {"name": d["name"]}
        | {
            f"{k}_{i}": v
            for i, d in enumerate(d["users"], 1)
            for k, v in d.items()
        }
        for d in data["results"]
    ]
).fillna("")

print(df.to_markdown())

打印:

name id_1 score_1 id_2 score_2
0 john smith 1 0.9 2 0.2
1 david jones 3 0.7
2 jane smith 5 0.8 6 0.3

或使用 ** 而不是 dict | dict:

df = pd.DataFrame(
    [
        {
            "name": d["name"],
            **{
                f"{k}_{i}": v
                for i, d in enumerate(d["users"], 1)
                for k, v in d.items()
            },
        }
        for d in data["results"]
    ]
).fillna("")

您可以快速使用:

>>> pd.json_normalize(json['results'], 'users', 'name')
  id  score         name
0  1    0.9   john smith
1  2    0.2   john smith
2  3    0.7  david jones
3  5    0.8   jane smith
4  6    0.3   jane smith

然后旋转你的数据框:

out = (pd.json_normalize(json['results'], 'users', 'name')
         .assign(colid=lambda x: x.groupby('name')['id'].cumcount().add(1).astype(str))
         .pivot('name', 'colid'))
out.columns = out.columns.to_flat_index().map('_'.join).rename(None)
print(out)

# Output
            id_1 id_2  score_1  score_2
name                                   
david jones    3  NaN      0.7      NaN
jane smith     5    6      0.8      0.3
john smith     1    2      0.9      0.2

您可以使用 json_normalize 然后 pivot:

#get the data you need into a dataframe
df = pd.json_normalize(json["results"],record_path="users",meta="name")

#create a counter to pivot
df["counter"] = df.groupby("name").cumcount().add(1)

#pivot to the desired structure
output = df.pivot("name","counter",["id","score"]).sort_values(by="counter",axis=1).rename_axis(None)

#collapse multi-level header to single level
output.columns = output.columns.map("{0[0]}_{0[1]}".format)

>>> output
            id_1 score_1 id_2 score_2
david jones    3     0.7  NaN     NaN
jane smith     5     0.8    6     0.3
john smith     1     0.9    2     0.2