【问题标题】:Dataframe has a column that is a list of dictionaries and I need to parse them into new coluimnsDataframe 有一列是字典列表,我需要将它们解析为新列
【发布时间】:2019-09-29 04:22:41
【问题描述】:

我有一个数据框,其中包含一个名为操作的列和一个字典列表。格式为 {source:int, action:string},我需要将其解析为每个新列,但每个操作单元格中的记录数是可变的。

数据如下所示:

|Id  |action                                                     |
|1   |[{"E": 4, "action": "views"}, {"A": 58, "action": "views"}]|
|2   |[{"A": 74, "action": "clicks"}]                            |

我希望它看起来像这样:

|Id|Source|Value|Action|
|1 |E     |4    |views |
|1 |A     |58   |views |
|2 |A     |74   |clicks|

action 列中的字典数量最多可达 10 个

我已经尝试了一些类似pandas DataFrame: normalize one JSON column and merge with other columns的解决方案

但它告诉我第一个解决方案没有正确调用 DataFrame,并且 str 没有第二个解决方案的属性值。除此之外,它也不是我需要的解决方案,因为我需要重命名列源并将 A/E/etc 值放入其中。

【问题讨论】:

  • 为什么你首先有一个带有字典的df?你能控制那一步吗?这个问题在上游得到了更好的解决
  • 我无法控制它,数据是以 csv 格式提供的,其中包含类似的列。
  • 我会使用 csv 模块并尝试在创建数据帧 tbh 之前使用它重新调整数据

标签: python pandas


【解决方案1】:

我将使用unnesting

yourdf=unnesting(df,['action']).reset_index(drop=True)
s=pd.DataFrame(yourdf.action.tolist()).reset_index().melt(['action','index']).dropna().set_index('index')

yourdf=pd.concat([yourdf,s],axis = 1)
yourdf
                          action  ID  action variable  value
0    {'E': 4, 'action': 'views'}   1   views        E    4.0
1   {'A': 58, 'action': 'views'}   1   views        A   58.0
2  {'A': 74, 'action': 'clicks'}   2  clicks        A   74.0

def unnesting(df, explode):
    idx = df.index.repeat(df[explode[0]].str.len())
    df1 = pd.concat([
        pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
    df1.index = idx

    return df1.join(df.drop(explode, 1), how='left')

【讨论】:

  • 这看起来不错,但我收到以下错误:Cannot cast array data from dtype('int64') to dtype('int32') based on the rule 'safe'
【解决方案2】:

我做了很长的路,但它应该给你你想要的输出。我只是在数据框的列中取消嵌套字典。这仅在字典仅采用您在示例数据框中显示的格式时才有效。

import pandas as pd

df = pd.DataFrame({'id': [1,2],
                   'action': [[{"E": 4, "action": "views"}, 
                               {"A": 58, "action": "views"}], 
                              [{"A": 74, "action": "clicks"}]]}
                 )

df_list = []
cols = ['id', 'source', 'value', 'action']
for x in df.index:
    for actions in df.loc[x, 'action']:
        row = []
        row.append(df.loc[x,'id'])
        for k,v in actions.items():
            if k == 'action':
                row.append(v)
            else:
                row.append(k)
                row.append(v)    
        df_list.append(row)
test = pd.DataFrame(df_list, columns=cols)

这给出了这个输出

   id source  value  action
0   1      E      4   views
1   1      A     58   views
2   2      A     74  clicks

【讨论】:

  • 这适用于示例数据,但是当我将它应用到我的整个数据集时,我得到错误“'str' has no attribute 'items'” on the line for k,v in actions.items( ):
  • 这意味着有一个操作列 roe 不是字典列表。代码假定所有数据都采用示例中的格式
  • 对,但是查看我的数据,一切都是字典列表。
【解决方案3】:

只需使用列表理解重建您的 DataFrame 数据:

更新: 所以action 列是一个字符串 字段,而不是 Python 数据结构。您可以使用 json.loads 对其进行预处理,这在从字符串中解析数据结构方面不太容易出错。

import json

# original dataframe
df = pd.DataFrame([
        (1, '[{"E": 4, "action": "views"}, {"A": 58, "action": "views"}]') 
      , (2, '[{"A": 74, "action": "clicks"}]') 
    ], 
    columns=['Id', 'action']
)

# new dataframe
df_new = pd.DataFrame(
    [ (id, k, v, i['action']) for id,act in zip(df.Id, df.action.apply(json.loads))
                              for i in act
                              for k,v in i.items() if k != 'action' 
    ],
    columns=['Id', 'Source', 'Value', 'Action']
)

print(new_df)
#   Id Source  Value  Action
#0   1      E      4   views
#1   1      A     58   views
#2   2      A     74  clicks

【讨论】:

  • 我仍然收到错误 str object has no attribute items 这没有意义,因为所有数据都是这种格式
  • @DBA108642,没注意到action是字符串字段,可以在处理Python数据结构之前使用json.loads解析。
  • 从技术上讲,它是一种对象类型。我试过在列上做一个 json.loads ,但它会抛出你无法将系列传递给该函数的错误
  • @DBA108642,使用df.action.apply(json.loads),你能试试我更新的答案吗?
  • 我真的非常爱你
猜你喜欢
  • 1970-01-01
  • 2019-05-06
  • 1970-01-01
  • 2018-01-06
  • 1970-01-01
  • 1970-01-01
  • 2018-02-11
  • 2017-08-15
  • 1970-01-01
相关资源
最近更新 更多