Python:将200k JSON文件读入数据帧
我对python非常陌生(<2周),被要求将提供给我的200k+JSON文件(按原样)读入一个数据库(使用python)。这些JSON文件具有简单的一级属性,在50->1000之间变化,但这50个属性是1000的子集 以下是一个json文件的片段:Python:将200k JSON文件读入数据帧,python,json,pandas,dataframe,Python,Json,Pandas,Dataframe,我对python非常陌生(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"
{
"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并创建一个(非常)大的数据帧。在过去的一周里,我一直在梳理堆栈溢出问题,并利用了一些经验教训,但我觉得必须有一种方法使这种方法更有效
下面是我到目前为止编写的代码——它运行起来很慢(我估计即使在消除了不必要的输入属性/以“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中),如果它更有效并且仍然允许我们实现上述目标的话
谢谢!您最关键的性能缺陷可能是:
database = pd.concat(df_list, axis=1).reset_index()
您可以在循环中执行此操作,每次向df_list
中添加一个以上的内容,然后再次执行concat。但是直到最后,此“数据库”变量才可用,因此您可以在循环之外执行此步骤一次
对于熊猫来说,循环中的“concat”是一个巨大的反模式。在循环中建立你的列表,concat立刻
第二件事是,您还应该使用Pandas来读取JSON文件:
保持简单。编写一个函数,该函数采用路径,调用pd.read\u json()
,删除不需要的行(series.str.startswith()
),等等
一旦工作正常,下一步将检查您是CPU受限(CPU使用率100%)还是I/O受限(CPU使用率远低于100%).我尝试以更简洁的方式复制您的方法,减少复制和附加。它适用于您提供的示例数据,但不知道您的数据集中是否存在进一步的复杂性。您可以尝试一下,我希望评论能有所帮助
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:
http://stackoverflow.com/questions/1747817/create-a-dictionary-with-list-comprehension-in-python
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)
请注意,您可以使用
json.load()
()直接读取文件。无需添加read()
并执行json.load()
。此外,为什么不将所有json读入一个大字典,然后将整个字典转换为一个数据框架(请参阅)您可以写入文件。谢谢Patrick!感谢提示,我会做出更改。这似乎不会对运行时间造成太大影响,但每一个效率都会有所帮助。@Patrick在我回应时刚刚看到了您的第二篇帖子。让我试一试。谢谢John,单是这一行的移动就极大地改变了运行时间。就我而言,我无法获得startswith()函数来处理此系列(我可以在这些文件上使用“read_json()”的唯一方法是:typ='series'和orient='records'w/o error)。-----------------------------------------------------------------------------------------如果不是data.str.startswith('results'):
(返回一个ValueError)Patrick,这很好!它完美地去除了列索引值。我遇到的问题(JSONDecodeError)是我是否取消了对排除条件的注释。它看起来像是结果resultdct[fili]
在转换为数据帧之前,有一个包含键/值对的json文件名和值的键。再次感谢您的任何想法/建议。@RDara在这样做时,您需要注释掉第一步,去掉排除项。这样做了吗?对不起,您指的是哪一步?基本上我在下面的行中添加了:resultdct[file]={k:v代表k,v在json.load(inputjson).items()中,如果不是k.startswith(“location”)}
(更改为“location”,因为它在上面的示例json文件中)…感谢您的耐心!我要补充的是,我最初花了90多分钟的时间在10k个文件的子集上减少到了几秒钟。刚刚在200k+的JSON文件上进行了测试(提取了一部分字段),这似乎也相当快。真是太感谢您了!!您可以将文件名传递到到_csv()
-不需要在那里打开()。