Pandas - 在数据框中的列内扩展嵌套的 json 数组

Posted

技术标签:

【中文标题】Pandas - 在数据框中的列内扩展嵌套的 json 数组【英文标题】:Pandas - expand nested json array within column in dataframe 【发布时间】:2018-05-25 16:52:27 【问题描述】:

我有一个 json 数据(来自 mongodb),其中包含数千条记录(因此是一个 json 对象的数组/列表),每个对象的结构如下所示:


   "id":1,
   "first_name":"Mead",
   "last_name":"Lantaph",
   "email":"mlantaph0@opensource.org",
   "gender":"Male",
   "ip_address":"231.126.209.31",
   "nested_array_to_expand":[
      
         "property":"Quaxo",
         "json_obj":
            "prop1":"Chevrolet",
            "prop2":"Mercy Streets"
         
      ,
      
         "property":"Blogpad",
         "json_obj":
            "prop1":"Hyundai",
            "prop2":"Flashback"
         
      ,
      
         "property":"Yabox",
         "json_obj":
            "prop1":"Nissan",
            "prop2":"Welcome Mr. Marshall (Bienvenido Mister Marshall)"
         
      
   ]

在数据帧中加载时,“nested_array_to_expand”是一个包含 json 的字符串(我在加载过程中确实使用了“json_normalize”)。预期的结果是获得一个包含 3 行的数据框(如上例所示)和嵌套对象的新列,如下所示:

index   email first_name gender  id      ip_address last_name  \
0  mlantaph0@opensource.org       Mead   Male   1  231.126.209.31   Lantaph   
1  mlantaph0@opensource.org       Mead   Male   1  231.126.209.31   Lantaph   
2  mlantaph0@opensource.org       Mead   Male   1  231.126.209.31   Lantaph   

  test.name                                      test.obj.ahah test.obj.buzz  
0     Quaxo                                      Mercy Streets     Chevrolet  
1   Blogpad                                          Flashback       Hyundai  
2     Yabox  Welcome Mr. Marshall (Bienvenido Mister Marshall)        Nissan  

我能够使用以下函数获得该结果,但速度极慢(1k 条记录大约需要 2 秒),因此我想改进现有代码或找到一种完全不同的方法来获得此结果。

def expand_field(field, df, parent_id='id'):
    all_sub = pd.DataFrame()
    # we need an id per row to be able to merge back dataframes
    # if no id, then we will create one based on index of rows
    if parent_id not in df:
        df[parent_id] = df.index

    # go through all rows and create a new dataframe with values
    for i, row in df.iterrows():
        try:
            sub = json_normalize(df[field].values[i])
            sub = sub.add_prefix(field + '.')
            sub['parent_id'] = row[parent_id]
            all_sub = all_sub.append(sub)
        except:
            print('crash')
            pass
    df = pd.merge(df, all_sub, left_on=parent_id, right_on='parent_id', how='left')
    #remove old columns
    del df["parent_id"]
    del df[field]
    #return expanded dataframe
    return df

非常感谢您的帮助。

===== 编辑回复评论 ====

从 mongodb 加载的数据是一个对象数组。 我用以下代码加载它:

data = json.loads(my_json_string)
df = json_normalize(data)

输出给我一个带有 df["nested_array_to_expand"] 作为 dtype 对象(字符串)的数据框

0    ['property': 'Quaxo', 'json_obj': 'prop1': '...
Name: nested_array_to_expand, dtype: object

【问题讨论】:

是dict或者json 感谢您查看。我编辑了这个问题,用示例和代码回答你。基本上我的 json 数据是一个对象数组,我加载然后用 json_normalize 规范化。那么“nest_array_to_expand”列就是一个 dtype 对象。 pd.json_normalize(data_list, "nested_array_to_expand", ['email', 'first_name', 'gender', 'id', 'ip_address', 'last_name']) 其中,data_list 是字典列表。 【参考方案1】:

我提出了一个有趣的答案,我认为使用pandas.json_normalize。 我用它来扩展嵌套的json——也许有更好的方法,但你绝对应该考虑使用这个功能。然后,您只需根据需要重命名列。

import io
from pandas import json_normalize

# Loading the json string into a structure
json_dict = json.load(io.StringIO(json_str))

df = pd.concat([pd.DataFrame(json_dict), 
                json_normalize(json_dict['nested_array_to_expand'])], 
                axis=1).drop('nested_array_to_expand', 1)

【讨论】:

谢谢。我会检查我的设置:) 嗨 Romain,不幸的是,该代码在我的设置中不起作用,因为我从我的 json 中获得了一个字典列表,而不是直接一个字典。 我想通了,并在下面发布了一个解决方案。【参考方案2】:

下面的代码就是你想要的。您可以使用 python 的内置列表函数展开嵌套列表并将其作为新数据框传递。 pd.DataFrame(list(json_dict['nested_col']))

您可能需要对此进行多次迭代,具体取决于数据的嵌套程度。

from pandas.io.json import json_normalize


df= pd.concat([pd.DataFrame(json_dict), pd.DataFrame(list(json_dict['nested_array_to_expand']))], axis=1).drop('nested_array_to_expand', 1)

【讨论】:

【参考方案3】:
import pandas as pd
import json

data = '''
[
  
   "id":1,
   "first_name":"Mead",
   "last_name":"Lantaph",
   "email":"mlantaph0@opensource.org",
   "gender":"Male",
   "ip_address":"231.126.209.31",
   "nested_array_to_expand":[
      
         "property":"Quaxo",
         "json_obj":
            "prop1":"Chevrolet",
            "prop2":"Mercy Streets"
         
      ,
      
         "property":"Blogpad",
         "json_obj":
            "prop1":"Hyundai",
            "prop2":"Flashback"
         
      ,
      
         "property":"Yabox",
         "json_obj":
            "prop1":"Nissan",
            "prop2":"Welcome Mr. Marshall (Bienvenido Mister Marshall)"
         
      
   ]
  
]
'''
data = json.loads(data)
result = pd.json_normalize(data, "nested_array_to_expand", 
                           ['email', 'first_name', 'gender', 'id', 'ip_address', 'last_name'])

结果


  property json_obj.prop1                                     json_obj.prop2  \
0    Quaxo      Chevrolet                                      Mercy Streets   
1  Blogpad        Hyundai                                          Flashback   
2    Yabox         Nissan  Welcome Mr. Marshall (Bienvenido Mister Marshall)   

                      email first_name gender id      ip_address last_name  
0  mlantaph0@opensource.org       Mead   Male  1  231.126.209.31   Lantaph  
1  mlantaph0@opensource.org       Mead   Male  1  231.126.209.31   Lantaph  
2  mlantaph0@opensource.org       Mead   Male  1  231.126.209.31   Lantaph  

更多关于json_normalize的信息: https://pandas.pydata.org/docs/reference/api/pandas.json_normalize.html

【讨论】:

以上是关于Pandas - 在数据框中的列内扩展嵌套的 json 数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Pandas 数据框中的特定位置插入一列? (更改熊猫数据框中的列顺序)

沿着 pandas 数据框中的列进行高效的跨步切片

用 Pandas 数据框中的行填充嵌套字典

在计算 Pandas 创建的数据框中的列的平均值时指定“跳过 NA”

使用 pandas 连接两个数据框中的不同列(并附加相似的列)

对 pandas 数据框中的列使用 map()