Pandas DataFrame 将 jsons 列列表转换为信息行,每个“id”

Posted

技术标签:

【中文标题】Pandas DataFrame 将 jsons 列列表转换为信息行,每个“id”【英文标题】:Pandas DataFrame turn a list of jsons column into informative row, per "id" 【发布时间】:2019-04-27 05:03:15 【问题描述】:

考虑以下DataFrame:

import pandas as pd

df = pd.DataFrame('id': [1, 2, 3],
               'json_col': [ ['aa' : 1, 'ab' : 1, 'aa' : 3, 'ab' : 2, 'ac': 6],
                             ['aa' : 1, 'ab' : 2, 'ac': 1, 'aa' : 5],
                             ['aa': 3, 'ac': 2] ])
df
Out[134]: 
   id                                           json_col
0   1  ['aa': 1, 'ab': 1, 'aa': 3, 'ab': 2, 'ac': 6]
1   2           ['aa': 1, 'ab': 2, 'ac': 1, 'aa': 5]
2   3                               ['aa': 3, 'ac': 2]

我们可以看到每个id都有一个json列表。

我希望,对于每个 'id' 及其列表中的每个对应 json,在 DataFrame 中都有一个 'row'。所以下面的DataFrame 会是这样的:

   id  aa   ab   ac
0   1   1  1.0  NaN
1   1   3  2.0  6.0
2   2   1  2.0  1.0
3   2   5  NaN  NaN
4   3   3  NaN  2.0

我们可以看到,id '1' 在它的列表中有 2 个对应的 json,因此它在新的DataFrame 中有 2 行

是否有使用 panda、numpy 或 json 功能的 Python 方法?


添加解决方案的运行时间

setup = """
import pandas as pd
df = pd.DataFrame('id': [1, 2, 3],
               'json_col': [ ['aa' : 1, 'ab' : 1, 'aa' : 3, 'ab' : 2, 'ac': 6],
                             ['aa' : 1, 'ab' : 2, 'ac': 1, 'aa' : 5],
                             ['aa': 3, 'ac': 2] ])
"""

s1 = """
df = pd.concat(
       [pd.DataFrame(j, index=[i]*len(j)) for i, j in enumerate(df['json_col'], 1)],
       sort=False
     )                             
"""

s2 = """
recs = df.apply(lambda x: [**'id': x.id, **d for d in x.json_col], axis=1).sum()
df2 = pd.DataFrame.from_records(recs)
"""

%timeit(s1, setup)
52.3 ns ± 2.6 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit(s2, setup)
50.6 ns ± 3.28 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

【问题讨论】:

【参考方案1】:

这是一种快速方法,将所有json_col 的字典列表转换为DataFrame 并将它们连接在一起并进行一些调整以创建id 列:

In [51]: df = pd.concat(
           [pd.DataFrame(j, index=[i]*len(j)) for i, j in enumerate(json_col, 1)],
           sort=False
         )

In [52]: df.index.name = 'id'

In [53]: df.reset_index()
Out[53]: 
   id  aa   ab   ac
0   1   1  1.0  NaN
1   1   3  2.0  6.0
2   2   1  2.0  1.0
3   2   5  NaN  NaN
4   3   3  NaN  2.0

【讨论】:

阿里解决方案工作速度更快,但它更python且易于理解,所以我会接受这个 @EranMoshe 不过,另一个答案很可能会慢一些。 我也是这么想的。但timeit 证明我错了。我可能会误用它。你想试试吗? @EranMoshe 哦,这很有趣!你能用基准更新你的问题吗?谢谢。 再次检查后,它们的运行大致相同。不过我会编辑它。【参考方案2】:

以下是完成此操作的一种简短方法,尽管我个人不认为它非常 Pythonic,因为代码有点难以阅读,而且性能不是很好,但对于小数据争论这个应该可以解决问题:

recs = df.apply(lambda x: [**'id': x.id, **d for d in x.json_col], axis=1).sum()
df2 = pd.DataFrame.from_records(recs)
# outputs:
   aa   ab   ac  id
0   1  1.0  NaN   1
1   3  2.0  6.0   1
2   1  2.0  1.0   2
3   5  NaN  NaN   2
4   3  NaN  2.0   3

工作原理:

    应用的 lambda 通过将 id: x.id 的内容合并到 x.json_col 中的字典列表中的每个字典来创建一个新字典(其中 x 是一行)。

    然后将其相加。由于对元素列表求和会将它们合并为一个大元素列表,因此 recs 具有以下形式

    ['id': 1, 'aa': 1, 'ab': 1,
     'id': 1, 'aa': 3, 'ab': 2, 'ac': 6,
     'id': 2, 'aa': 1, 'ab': 2, 'ac': 1,
     'id': 2, 'aa': 5,
     'id': 3, 'aa': 3, 'ac': 2]
    

    然后简单地从记录构造一个新的数据框。

【讨论】:

以上是关于Pandas DataFrame 将 jsons 列列表转换为信息行,每个“id”的主要内容,如果未能解决你的问题,请参考以下文章

Pandas:将 DataFrame 与嵌套数组结合或合并 JSON 输出

将 JSON 数组嵌套到 Python Pandas DataFrame

将 Python JSON 文件转换为 Pandas DataFrame

Pandas DataFrame 将 jsons 列列表转换为信息行,每个“id”

将 Pandas Dataframe 转换为表记录的嵌套 JSON

如何将 pandas DataFrame 行保存为 JSON 字符串?