如何将 Pandas DataFrame 中的字典列表展平为几列?

Posted

技术标签:

【中文标题】如何将 Pandas DataFrame 中的字典列表展平为几列?【英文标题】:How to flatten a list of dicts from a Pandas DataFrame into several columns? 【发布时间】:2018-04-30 01:44:26 【问题描述】:

我有一个看起来像这样的 pandas 数据框:

User | Query|                                 Filters                 
----------------------------------------------------------------------------------------- 
1    |  abc | [u'Op': u'and', u'Type': u'date', u'Val': u'1992',u'Op': u'and', u'Type': u'sex', u'Val': u'F']
1    |  efg | [u'Op': u'and', u'Type': u'date', u'Val': u'2000',u'Op': u'and', u'Type': u'col', u'Val': u'Blue'] 
1    |  fgs | [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'col', u'Val': u'Red']        
2    |  hij | [u'Op': u'and', u'Type': u'date', u'Val': u'2002']  
2    |  dcv | [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'sex', u'Val': u'F']     
2    |  tyu | [u'Op': u'and', u'Type': u'date', u'Val': u'1999',u'Op': u'and', u'Type': u'col', u'Val': u'Yellow']     
3    |  jhg | [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'sex', u'Val': u'M']    
4    |  mlh | [u'Op': u'and', u'Type': u'date', u'Val': u'2001']  

我期望的结果:

User| Query |  date | sex | col
-------------------------------- 
1   | abc   | 1992  |  F  |
1   | efg   | 2000  |     | Blue
1   | fgs   | 2001  |     | Red
2   | hij   | 2002  |     |
2   | dcv   | 2001  |  F  |
2   | tyu   | 1999  |     | Yellow
3   | jhg   | 2001  |     |
4   | mlh   | 2001  |  H  |

我将 pandas 0.21.0 与 python 2.7 一起使用。

示例数据:

df = pd.DataFrame(['user': 1,'query': 'abc', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'1992',u'Op': u'and', u'Type': u'sex', u'Val': u'F'],
              'user': 1,'query': 'efg', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2000',u'Op': u'and', u'Type': u'col', u'Val': u'Blue'],
              'user': 1,'query': 'fgs', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'col', u'Val': u'Red'],
              'user': 2 ,'query': 'hij', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2002'],
              'user': 2 ,'query': 'dcv', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'sex', u'Val': u'F'],
              'user': 2 ,'query': 'tyu', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'1999',u'Op': u'and', u'Type': u'col', u'Val': u'Yellow'],
              'user': 3 ,'query': 'jhg', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'sex', u'Val': u'M'],
              'user': 4 ,'query': 'mlh', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'2001'],
             ])

我尝试了很多解决方案:

Handling of nested JSON records Pandas read nested json

任何建议将不胜感激!

【问题讨论】:

【参考方案1】:

假设您已经按照 MCWE 中的定义导入了数据:

data = ['user': 1,'query': 'abc', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'1992',u'Op': u'and', u'Type': u'sex', u'Val': u'F'],
              'user': 1,'query': 'efg', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2000',u'Op': u'and', u'Type': u'col', u'Val': u'Blue'],
              'user': 1,'query': 'fgs', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'col', u'Val': u'Red'],
              'user': 2 ,'query': 'hij', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2002'],
              'user': 2 ,'query': 'dcv', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'sex', u'Val': u'F'],
              'user': 2 ,'query': 'tyu', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'1999',u'Op': u'and', u'Type': u'col', u'Val': u'Yellow'],
              'user': 3 ,'query': 'jhg', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'sex', u'Val': u'M'],
              'user': 4 ,'query': 'mlh', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'2001'],
             ]

然后,您正在寻找 Pandas json_normalize 数据规范化方法:

from pandas.io.json import json_normalize
df = json_normalize(data, 'Filters', ['query', 'user'])

它返回一个标准化的 DataFrame 版本,其中您的 json 列扩展为同名类型的列:

     Op  Type     Val  user query
0   and  date    1992     1   abc
1   and   sex       F     1   abc
2   and  date    2000     1   efg
3   and   col    Blue     1   efg
4   and  date    2001     1   fgs
5   and   col     Red     1   fgs
6   and  date    2002     2   hij
7   and  date    2001     2   dcv
8   and   sex       F     2   dcv
9   and  date    1999     2   tyu
10  and   col  Yellow     2   tyu
11  and  date    2001     3   jhg
12  and   sex       M     3   jhg
13  and  date    2001     4   mlh

现在,您将 pivot 您的 DataFrame 将类型模式转换为列:

df = df.pivot_table(index=['user', 'query', 'Op'], columns='Type', aggfunc='first')

它导致:

                   Val            
Type               col  date   sex
user query Op                     
1    abc   and    None  1992     F
     efg   and    Blue  2000  None
     fgs   and     Red  2001  None
2    dcv   and    None  2001     F
     hij   and    None  2002  None
     tyu   and  Yellow  1999  None
3    jhg   and    None  2001     M
4    mlh   and    None  2001  None

最后,你可以清理和重置索引,如果它们打扰你:

df.columns = df.columns.droplevel(0)
df.reset_index(inplace=True)

返回您请求的 MCVE 输出:

Type  user query   Op     col  date   sex
0        1   abc  and    None  1992     F
1        1   efg  and    Blue  2000  None
2        1   fgs  and     Red  2001  None
3        2   dcv  and    None  2001     F
4        2   hij  and    None  2002  None
5        2   tyu  and  Yellow  1999  None
6        3   jhg  and    None  2001     M
7        4   mlh  and    None  2001  None

不是列

在这个最终的 DataFrame 中,第一列似乎被称为Type,但事实并非如此。它是一个没有名称的整数索引:

df.index
RangeIndex(start=0, stop=8, step=1)

列索引称为Type,它不包含任何称为Type 的模态(因此没有具有此名称的列)。

df.columns
Index(['user', 'query', 'Op', 'col', 'date', 'sex'], dtype='object', name='Type')

这就是为什么您不能删除列Typepivot_table 中使用的列),因为它不存在。

如果你想删除这个假列,你需要为行创建一个新的索引:

df.set_index(['user', 'query'], inplace=True)

如果 Column index Name 困扰你,你可以重置它:

df.columns.name = None

它导致:

             Op     col  date   sex
user query                         
1    abc    and    None  1992     F
     efg    and    Blue  2000  None
     fgs    and     Red  2001  None
2    dcv    and    None  2001     F
     hij    and    None  2002  None
     tyu    and  Yellow  1999  None
3    jhg    and    None  2001     M
4    mlh    and    None  2001  None

创建新索引时始终检查它的唯一性是一个很好的做法:

df.index.is_unique
True

文件中的数据

如果你的数据在一个文件中,你应该首先使用 PSL json 模块将它导入到一个变量中:

import json
with open(path) as file:
    data = json.load(file)

这可以解决问题,然后回到我的答案的开头。

【讨论】:

@landercy 认为 DataFrame 是读取 json 文件的结果:df = pd.read_json(path,lines='True') 我收到此错误:TypeError: string indices must be integers @Omar14,我已经用你的 MCWE 数据建立了我的答案,如果有问题,应该在导入阶段发生。您可以使用 PSL json.loads 来馈送 json_normalize @Omar14 添加了代码来完成您的请求。如果适合您,请标记为答案。 @jlandercy 你知道如何删除类型列吗?我试过 df.drop(columns=['Type'])【参考方案2】:
import pandas as pd

df = pd.DataFrame(['user': 1,'query': 'abc', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'1992',u'Op': u'and', u'Type': u'sex', u'Val': u'F'],
              'user': 1,'query': 'efg', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2000',u'Op': u'and', u'Type': u'col', u'Val': u'Blue'],
              'user': 1,'query': 'fgs', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'col', u'Val': u'Red'],
              'user': 2 ,'query': 'hij', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2002'],
              'user': 2 ,'query': 'dcv', 'Filters': [u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'sex', u'Val': u'F'],
              'user': 2 ,'query': 'tyu', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'1999',u'Op': u'and', u'Type': u'col', u'Val': u'Yellow'],
              'user': 3 ,'query': 'jhg', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'2001',u'Op': u'and', u'Type': u'sex', u'Val': u'M'],
              'user': 4 ,'query': 'mlh', 'Filters':[u'Op': u'and', u'Type': u'date', u'Val': u'2001'],
             ])

def func(x):
    date = x[0]['Val']
    sex = ''
    col = ''
    if len(x) > 1:
        if x[1]['Val'] in ['F','M']:
            sex = x[1]['Val']
        else:
            col = x[1]['Val']      
    return pd.Series([date,sex,col])

df[['date','sex','color']] = df['Filters'].apply(func)

df

输出(不显示过滤器):

  query  user  date sex   color
0   abc     1  1992   F        
1   efg     1  2000        Blue
2   fgs     1  2001         Red
3   hij     2  2002            
4   dcv     2  2001   F        
5   tyu     2  1999      Yellow
6   jhg     3  2001   M        
7   mlh     4  2001            

【讨论】:

以上是关于如何将 Pandas DataFrame 中的字典列表展平为几列?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Pandas 系列中的多个字典键转换为 DataFrame 中的列?

将字典嵌套在另一个字典中,按 Pandas Dataframe 中的值分组

将 pandas.DataFrame 转换为 Python 中的字典列表

如何将嵌套字典转换为 pandas DataFrame?

python 将字典值映射到Pandas中的Dataframe值(Python)

如何将 pandas DataFrame 转换为省略 NaN 值的字典列表?