使用 Pandas 在巨大的 CSV 中解析带有嵌套值的 JSON 列

Posted

技术标签:

【中文标题】使用 Pandas 在巨大的 CSV 中解析带有嵌套值的 JSON 列【英文标题】:Using Pandas to parse a JSON column w/nested values in a huge CSV 【发布时间】:2018-12-02 01:27:15 【问题描述】:

我有一个巨大的 CSV 文件(3.5GB 并且每天都在变大),其中包含正常值和一个名为“元数据”的列,其中包含嵌套的 JSON 值。我的脚本如下,目的只是将 JSON 列转换为每个键值对的普通列。我正在使用 Python3(Anaconda;Windows)。

import pandas as pd
import numpy as np
import csv
import datetime as dt

from pandas.io.json import json_normalize

for df in pd.read_csv("source.csv", engine='c', 
    dayfirst=True, 
    encoding='utf-8', 
    header=0,
    nrows=10,
    chunksize=2,
    converters='Metadata':json.loads):

    ## parsing code comes here

    with open("output.csv", 'a', encoding='utf-8') as ofile:
        df.to_csv(ofile, index=False, encoding='utf-8')

并且该列具有以下格式的 JSON:

  
   "content_id":"xxxx",
   "parental":"F",
   "my_custom_data":  
      "GroupId":"NA",
      "group":null,
      "userGuid":"xxxxxxxxxxxxxx",
      "deviceGuid":"xxxxxxxxxxxxx",
      "connType":"WIFI",
      "channelName":"VOD",
      "assetId":"xxxxxxxxxxxxx",
      "GroupName":"NA",
      "playType":"VOD",
      "appVersion":"2.1.0",
      "userEnvironmentContext":"",
      "vodEncode":"H.264",
      "language":"English"
   

所需的输出是将上述所有键值对作为列。数据框将具有其他非 JSON 列,我需要添加从上述 JSON 解析的列。我尝试了json_normalize,但我不确定如何将json_normalize 应用到Series 对象,然后将其转换(或分解)为多个列。

【问题讨论】:

【参考方案1】:

直接在系列上使用json_normalize(),然后使用pandas.concat()将新数据框与现有数据框合并:

pd.concat([df, json_normalize(df['Metadata'])])

如果您不再需要包含 JSON 数据结构的旧列,可以添加 .drop('Metadata', axis=1)

my_custom_data 嵌套字典生成的列将以my_custom_data. 为前缀。如果嵌套字典 in 中的所有名称都是唯一的,则可以使用 DataFrame.rename() operation 删除该前缀:

json_normalize(df['Metadata']).rename(
    columns=lambda n: n[15:] if n.startswith('my_custom_data.') else n)

如果您使用其他方法将每个字典值转换为扁平结构(例如,使用flatten_json,那么您想使用Series.apply() 处理每个值,然后将每个结果字典作为pandas.Series() 返回对象:

def some_conversion_function(dictionary):
    result = something_that_processes_dictionary_into_a_flat_dict(dictionary)
    return pd.Series(something_that_processes_dictionary_into_a_flat_dict)

然后您可以将Series.apply() 调用的结果(这将是一个数据帧)连接回您的原始数据帧:

pd.concat([df, df['Metadata'].apply(some_conversion_function)])

【讨论】:

感谢您的回复...我循环它的原因是 read_csv 在您使用 chunksize=xxx 时返回一个 Iterator .... 同时,我也收到一个 KeyError: 'group ' 来自 json_normalize。 @A.Ali:啊,我错了,我错过了那里的 chunksize 参数。 显然,json_normalize 不能很好地处理我的 json。它给了我一个 KeyError:'group'。使用 flatten_json 库我取得了更好的成功。现在我有一个 python dict,需要使用 dict 创建一个数据框,然后将其附加到现有的数据框。 @A.Ali:如果你有一个平面字典(所有值都是标量),只需使用 df[columnname].apply(pd.Series) 直接从中创建一个 Series();这将返回一个新的数据框,使用 pd.concat() 就像使用 json_normalize() 一样。 def xflatten(js): return pd.Series(flatten_json.flatten(js)),然后是pd.concat([df.drop(['Metadata'], axis=1), df.Metadata.apply(xflatten)])

以上是关于使用 Pandas 在巨大的 CSV 中解析带有嵌套值的 JSON 列的主要内容,如果未能解决你的问题,请参考以下文章

有效地读取巨大的 csv 文件?

使用 pandas 读取“csv”文件时解析日期时间

如何操作一个巨大的 csv 文件(> 12GB)?

如何使用带有 gzip 压缩选项的 pandas read_csv 读取 tar.gz 文件?

使用 pandas 读取带有 numpy 数组的 csv

使用 pandas 读取带有 numpy 数组的 csv