将Pandas中的字典列表拆分为行

9

请看以下内容:

                                  items, name
0   { [{'a': 2, 'b': 1}, {'a': 4, 'b': 3}], this }
1   { [{'a': 2, 'b': 1}, {'a': 4, 'b': 3}], that }

但是我想要将字典对象的列表展开(展平?)为实际的行,如下所示:
    a, b, name
0   { 2, 1, this}
1   { 4, 3, this}
0   { 2, 1, that}
1   { 4, 3, that}

我一直尝试使用melt,但没有成功,有什么想法或建议吗?

生成DataFrame的数据:

data = {'items': [[{'a': 2, 'b': 1}, {'a': 4, 'b': 3}], [{'a': 2, 'b': 1}, {'a': 4, 'b': 3}]], 'name': ['this', 'that']}

2
一开始就是2列吗? - piRSquared
是的,两列。 - De La Brez
谢谢大家! - De La Brez
6个回答

6

使用concat的另一种更清晰的方法:

In [11]: pd.concat(df.group.apply(pd.DataFrame).tolist(), keys=df["name"])
Out[11]:
        a  b
name
this 0  2  1
     1  4  3
that 0  2  1
     1  4  3

In [12]: pd.concat(df.group.apply(pd.DataFrame).tolist(), 
                        keys=df["name"]).reset_index(level="name")
Out[12]:
   name  a  b
0  this  2  1
1  this  4  3
0  that  2  1
1  that  4  3

2
看起来不错,但是我收到了这个 AttributeError: 'DataFrame' object has no attribute 'group' 的错误信息?也许是我的 pandas 版本问题? - De La Brez
pd.concat(df['items'].apply(pd.DataFrame).tolist(), keys=df["name"]).reset_index(level="name") - De La Brez

4
另一种解决方案是使用“name”进行set_indexexplode“items”。然后将结果系列转换为数据框。
s = df.set_index('name')['items'].explode()
out = pd.DataFrame(s.tolist(), index=s.index).reset_index()

输出:

   name  a  b
0  this  2  1
1  this  4  3
2  that  2  1
3  that  4  3

看起来,set_index + explode + DataFrame (至少对于OP的数据而言)比其他答案中提供的选项更快。

%timeit -n 1000 out = pd.concat(df['items'].apply(pd.DataFrame).tolist(), keys=df["name"]).reset_index()
%timeit -n 1000 ab = pd.DataFrame.from_dict(np.concatenate(df['items']).tolist()); lens = df['items'].str.len(); rest = df.drop('items', axis=1).iloc[df.index.repeat(lens)].reset_index(drop=True); out = ab.join(rest)
%timeit -n 1000 out = pd.concat([pd.DataFrame(df1.iloc[0]) for x,df1 in df.groupby('name')['items']],keys=df.name).reset_index().drop('level_1',axis=1)
%timeit -n 1000 s = df.set_index('name')['items'].explode(); out = pd.DataFrame(s.tolist(), index=s.index).reset_index()

2.5 ms ± 29.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.75 ms ± 12.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3.82 ms ± 433 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.46 ms ± 68 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

3
ab = pd.DataFrame.from_dict(np.concatenate(df['items']).tolist())
lens = df['items'].str.len()
rest = df.drop('items', 1).iloc[df.index.repeat(lens)].reset_index(drop=True)
ab.join(rest)

   a  b  name
0  2  1  this
1  4  3  this
2  2  1  that
3  4  3  that

3
pd.concat([pd.DataFrame(df1.iloc[0]) for x,df1 in df.groupby('name').group],keys=df.name)\
     .reset_index().drop('level_1',1)
Out[63]: 
   name  a  b
0  this  2  1
1  this  4  3
2  that  2  1
3  that  4  3

数据输入

df = pd.DataFrame({ "group":[[{'a': 2, 'b': 1}, {'a': 4, 'b': 3}],[{'a': 2, 'b': 1}, {'a': 4, 'b': 3}]],
                   "name": ['this', 'that']})

0
tmp_list = list()
for index, row in a.iterrows():
    for list_item in row['items']:
        tmp_list.append(dict(list_item.items()+[('name', row['name'])]))
pd.DataFrame(tmp_list)

   a  b  name
0  2  1  this
1  4  3  this
2  2  1  that
3  4  3  that

0

处理类似情况的更一般方法

  1. explode函数将项拆分为行
  2. 使用pd.json_normalize将字典转换为Pandas列
  3. 连接原始数据帧和使用json_normalize的列,并删除原始项列
In [1]: import pandas as pd

In [2]: data = {'items': [[{'a': 2, 'b': 1}, {'a': 4, 'b': 3}], [{'a': 2, 'b': 1}, {'a': 4, 'b': 3}]], 'name': ['this', 'that']}

In [3]: df = pd.DataFrame(data).explode('items')

In [4]: df
Out[4]: 
              items  name
0  {'a': 2, 'b': 1}  this
0  {'a': 4, 'b': 3}  this
1  {'a': 2, 'b': 1}  that
1  {'a': 4, 'b': 3}  that

In [5]: df = df.reset_index(drop=True)  # align source table and items

In [6]: df
Out[6]: 
              items  name
0  {'a': 2, 'b': 1}  this
1  {'a': 4, 'b': 3}  this
2  {'a': 2, 'b': 1}  that
3  {'a': 4, 'b': 3}  that

In [7]: pd.json_normalize(df['items']) # just to illustrate what is happen
Out[7]: 
   a  b
0  2  1
1  4  3
2  2  1
3  4  3

In [8]: df.join(
   ...:     pd.json_normalize(df['items']), # "explode" dict to separate columns
   ...:     rsuffix='_right' # just in case if you have overlapping column names
   ...: ).drop(columns=['items']) # delete original column
Out[8]: 
   name  a  b
0  this  2  1
0  this  2  1
1  that  4  3
1  that  4  3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接