Python:将200k个JSON文件读入Pandas数据帧

4
我是一个非常新的Python用户(不到两周),被要求使用Python将我提供的200k+ JSON文件读入单个数据库中。这些JSON文件具有一级平面属性,每个文件从50到1000不等,但这50个是1000个子集之一。
以下是JSON文件的片段:
{
"study_type" : "Observational",
"intervention.intervention_type" : "Device",
"primary_outcome.time_frame" : "24 months",
"primary_completion_date.type" : "Actual",
"design_info.primary_purpose" : "Diagnostic",
"design_info.secondary_purpose" : "Intervention",
"start_date" : "January 2014",
"end_date" : "March 2014",
"overall_status" : "Completed",
"location_countries.country" : "United States",
"location.facility.name" : "Generic Institution",
}

我们的目标是将这些JSON文件的主数据库进行清理,对每个列运行描述性统计并创建最终的、清洁的数据库。
我的背景是SAS,所以我想使用pandas创建一个(非常)大的数据帧。我过去一周一直在查阅stack overflow,并利用了一些经验,但我感觉一定有一种方法可以使它变得更加高效。
下面是我迄今为止编写的代码——它可以运行,但速度非常慢(即使在消除以“result”开头的不需要的输入属性/列之后,我估计它需要几天甚至几周时间才能运行)。此外,我将字典转换为最终表格的笨拙方式留下了列索引号在列名之上的问题,我无法解决这个问题。
import json, os
import pandas as pd    
from copy import deepcopy

path_to_json = '/home/ubuntu/json_flat/'

#Gets list of files in directory with *.json suffix
list_files = [pos_json for pos_json in os.listdir(path_to_json) if pos_json.endswith('.json')]

#Initialize series
df_list = []

#For every json file found
for js in list_files:

    with open(os.path.join(path_to_json, js)) as data_file:
        data = json.loads(data_file.read())                         #Loads Json file into dictionary
        data_file.close()                                           #Close data file / remove from memory

        data_copy = deepcopy(data)                                  #Copies dictionary file
        for k in data_copy.keys():                                  #Iterate over copied dictionary file
            if k.startswith('result'):                              #If field starts with "X" then delete from dictionary
                del data[k]
        df = pd.Series(data)                                        #Convert Dictionary to Series
        df_list.append(df)                                          #Append to empty series  
        database = pd.concat(df_list, axis=1).reset_index()         #Concatenate series into database

output_db = database.transpose()                                    #Transpose rows/columns
output_db.to_csv('/home/ubuntu/output/output_db.csv', mode = 'w', index=False)

任何想法和建议都非常感激。如果在Python中使用不同的技术或方法可以更有效地实现我们上述的目标,我完全可以接受。谢谢!

请注意,您可以使用json.load()文档)直接读取文件。无需添加read()并执行json.loads() - patrick
另外,为什么不将所有的JSON文件读入一个大字典中,然后将整个字典转换为Pandas的DataFrame(参见这里),以便您可以将其写入文件。 - patrick
谢谢Patrick!感谢您的提示,我会进行更改。它似乎并不会对运行时间产生太大影响,但每一点效率都有帮助。 - RDara
@patrick,我正在回复您的帖子时看到了您的第二个帖子。让我试一下。 - RDara
2个回答

3

我尝试以更简洁的方式复制您的方法,减少了拷贝和追加。它可以使用您提供的示例数据,但不知道您的数据集中是否还有其他细节。您可以尝试一下,希望注释有所帮助。

import json
import os
import pandas
import io


path_to_json = "XXX"

list_files = [pos_json for pos_json in os.listdir(path_to_json) if pos_json.endswith('.json')]

#set up an empty dictionary
resultdict = {}

for fili in list_files:
    #the with avoids the extra step of closing the file
    with open(os.path.join(path_to_json, fili), "r") as inputjson:
        #the dictionary key is set to filename here, but you could also use e.g. a counter
        resultdict[fili] = json.load(inputjson)
        """
        you can exclude stuff here or later via dictionary comprehensions: 
        https://dev59.com/AHI-5IYBdhLWcg3wpaJ1
        e.g. as in your example code
        resultdict[fili] = {k:v for k,v in json.load(inputjson).items() if not k.startswith("result")}
        """

#put the whole thing into the DataFrame     
dataframe = pandas.DataFrame(resultdict)

#write out, transpose for desired format
with open("output.csv", "w") as csvout:
    dataframe.T.to_csv(csvout)

Patrick,这个很好用!它可以很好地去除列索引值。但是我遇到了一个问题(JSONDecodeError),如果我取消注释排除条件的话。看起来resultdict[fili]的结果有一个json文件名的键和在转换为数据帧之前包含键/值对的值。再次感谢您的想法/建议。 - RDara
1
@RDara 在执行此操作时,您需要将第一步注释掉而不排除它。这样做可以解决问题吗? - patrick
@RDara 我的意思是这个必须被移除:resultdict[fili] = json.load(inputjson)。另外,迭代器是 fili 而不是 file;不是说它是个好名字,但需要保持一致,并尽量避免使用 Python 内部名称,比如 _file_ - patrick
1
只是补充一下,最初花费我90多分钟的事情,在10k个文件的子集上缩短到了几秒钟。刚刚在完整的200k+ JSON文件上进行了测试(提取字段的子集),也似乎相当快速地完成了。无法感谢你们的付出!! - RDara
2
你可以直接将文件名传递给 to_csv() 函数 - 不需要在那里使用 open() - John Zwinck
显示剩余3条评论

1
您最关键的性能缺陷可能是这个:
database = pd.concat(df_list, axis=1).reset_index()

您需要在循环中执行此操作,每次将一个新元素添加到df_list中,然后再次连接。但是在结束之前没有使用这个“数据库”变量,所以您可以在循环外部执行此步骤一次。
使用Pandas,“concat”循环是一个巨大的反模式。在循环中构建列表,然后进行连接。
第二件事是您应该使用Pandas读取JSON文件:http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_json.html 保持简单。编写一个函数,接收路径,调用pd.read_json(),删除不需要的行(series.str.startswith()),等等。
一旦您做得很好,下一步就是检查您是否受到CPU限制(CPU使用率为100%)或I / O限制(CPU使用率远低于100%)。

谢谢John,仅仅那一行代码就显著地改变了运行时间。我无论如何都无法让startswith()函数与这个系列一起工作(我唯一能够在这些文件上使用"read_json()"的方法是:typ='series'和orient='records',否则会出错)。-----------------------------------------------------------------if not data.str.startswith('results'):(返回一个ValueError) - RDara

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接