【问题标题】:Optimize parsing file with JSON objects in pandas dataframe, where keys may be missing in some rows使用 pandas 数据框中的 JSON 对象优化解析文件,其中某些行中可能缺少键
【发布时间】:2016-06-09 06:02:03
【问题描述】:

我正在优化下面的代码,大约需要 5 秒,这对于只有 1000 行的文件来说太慢了。

我有一个大文件,其中每一行都包含有效的 JSON,每个 JSON 如下所示(实际数据要大得多且嵌套,因此我使用此 JSON sn-p 进行说明):

  {"location":{"town":"Rome","groupe":"Advanced",
    "school":{"SchoolGroupe":"TrowMet", "SchoolName":"VeronM"}},
    "id":"145",
    "Mother":{"MotherName":"Helen","MotherAge":"46"},"NGlobalNote":2,
    "Father":{"FatherName":"Peter","FatherAge":"51"},
    "Teacher":["MrCrock","MrDaniel"],"Field":"Marketing",
     "season":["summer","spring"]}

我需要解析这个文件,以便从每个 JSON 中仅提取一些键值,以获得结果数据帧:

Groupe      Id   MotherName   FatherName
Advanced    56   Laure         James
Middle      11   Ann           Nicolas
Advanced    6    Helen         Franc

但是我在数据框中需要的一些键在一些 JSON 对象中丢失了,所以我应该验证键是否存在,如果不存在,则用 Null 填充相应的值。我使用以下方法:

df = pd.DataFrame(columns=['group', 'id', 'Father', 'Mother'])
with open (path/to/file) as f:
    for chunk in f:
        jfile = json.loads(chunk)

        if 'groupe' in jfile['location']:
            groupe = jfile['location']['groupe']
        else:
            groupe=np.nan

        if 'id' in jfile:
            id = jfile['id']
        else:
            id = np.nan

        if 'MotherName' in jfile['Mother']:
            MotherName = jfile['Mother']['MotherName']
        else:
            MotherName = np.nan

        if 'FatherName' in jfile['Father']:
            FatherName = jfile['Father']['FatherName']
        else: 
            FatherName = np.nan

        df = df.append({"groupe":group, "id":id, "MotherName":MotherName, "FatherName":FatherName},
            ignore_index=True)

我需要将整个 1000 行文件的运行时间优化为

【问题讨论】:

标签: python json performance pandas memory


【解决方案1】:

关键部分是不要将每一行附加到循环中的数据帧。您希望将集合保存在列表或 dict 容器中,然后一次将它们连接起来。您还可以使用简单的 get 简化您的 if/else 结构,如果在字典中找不到该项目,则返回默认值(例如 np.nan)。

with open (path/to/file) as f:
    d = {'group': [], 'id': [], 'Father': [], 'Mother': []}
    for chunk in f:
        jfile = json.loads(chunk)
        d['groupe'].append(jfile['location'].get('groupe', np.nan))
        d['id'].append(jfile.get('id', np.nan))
        d['MotherName'].append(jfile['Mother'].get('MotherName', np.nan))
        d['FatherName'].append(jfile['Father'].get('FatherName', np.nan))

    df = pd.DataFrame(d)

【讨论】:

  • 您的答案很好,但是在将字典转换为熊猫数据框时出现错误TypeError: list indices must be integers, not str
  • 听起来数据可能有问题。尝试从每一列中创建一个 DataFrame,看看是否可以隔离问题。
【解决方案2】:

如果您可以在初始化期间一步构建数据框,您将获得最佳性能。 DataFrame.from_record 采用一系列元组,您可以从一次读取一条记录的生成器中提供这些元组。您可以使用get 更快地解析数据,它会在未找到该项目时提供默认参数。我创建了一个名为dummy 的空dict 来传递中间gets,这样你就知道链式get 会起作用。

我创建了一个包含 1000 条记录的数据集,在我糟糕的笔记本电脑上,时间从 18 秒变为 0.06 秒。挺好的。

import numpy as np
import pandas as pd
import json
import time

def extract_data(data):
    """ convert 1 json dict to records for import"""
    dummy = {}
    jfile = json.loads(data.strip())
    return (
        jfile.get('location', dummy).get('groupe', np.nan), 
        jfile.get('id', np.nan),
        jfile.get('Mother', dummy).get('MotherName', np.nan),
        jfile.get('Father', dummy).get('FatherName', np.nan))

start = time.time()
df = pd.DataFrame.from_records(map(extract_data, open('file.json')),
    columns=['group', 'id', 'Father', 'Mother'])
print('New algorithm', time.time()-start)

#
# The original way
#

start= time.time()
df=pd.DataFrame(columns=['group', 'id', 'Father', 'Mother'])
with open ('file.json') as f:
      for chunk in f:
           jfile=json.loads(chunk)
           if 'groupe' in jfile['location']:
               groupe=jfile['location']['groupe']
           else:
               groupe=np.nan
           if 'id' in jfile:
                id=jfile['id']
           else:
                id=np.nan
           if 'MotherName' in jfile['Mother']:
                MotherName=jfile['Mother']['MotherName']
           else:
                MotherName=np.nan
           if 'FatherName' in jfile['Father']:
                FatherName=jfile['Father']['FatherName']
           else: 
                FatherName=np.nan
           df = df.append({"groupe":groupe,"id":id,"MotherName":MotherName,"FatherName":FatherName},
            ignore_index=True)
print('original', time.time()-start)

【讨论】:

  • 我有AttributeError: 'list' object has no attribute 'get'这个方法!不要忘记我每行都有一个带有 json 的文件,也许这是一个问题。所以我需要遍历这些行来解析每个 json
  • 所以整个文件不是json本身,而是这个文件的每一行都是有效的json
  • 它可以工作,除了字典而不是嵌套的 json 的情况!在这种情况下如何使用 .get 方法? @tdelaney
  • 我不确定“嵌套 json”是什么意思。它是单个 json 编码的字符串吗?也许你可以解码它并用解码的结构替换字符串。
  • Amanda 请编辑您的问题,为这些极端情况添加示例数据。 JSON 中的解析问题很难重现... ;-)
猜你喜欢
  • 2018-01-01
  • 1970-01-01
  • 2020-06-04
  • 2018-05-01
  • 1970-01-01
  • 2020-05-15
  • 2021-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多