Json 从清单中读取S3文件并使用Pandas并行处理它们

Json 从清单中读取S3文件并使用Pandas并行处理它们,json,pandas,amazon-web-services,amazon-s3,boto3,Json,Pandas,Amazon Web Services,Amazon S3,Boto3,我需要使用清单文件从S3读取大约50k。我必须将每个(JSON)文件的内容读入一个数据帧并处理这些文件(将它们规范化为数据库表)。现在我有一个工作代码,处理50k文件大约需要15个小时。我必须把这当作日常工作。有什么方法可以并行处理大量文件,或者有什么更好的方法可以加快处理速度 用代码更新问题 import json import pandas as pd import os import gzip import boto3 from datetime import datetime,tim

我需要使用清单文件从S3读取大约50k。我必须将每个(JSON)文件的内容读入一个数据帧并处理这些文件(将它们规范化为数据库表)。现在我有一个工作代码,处理50k文件大约需要15个小时。我必须把这当作日常工作。有什么方法可以并行处理大量文件,或者有什么更好的方法可以加快处理速度

用代码更新问题

import json 
import pandas as pd 
import os
import gzip
import boto3
from datetime import datetime,timezone,timedelta


session = boto3.session.Session()
s3 = session.resource('s3')
client = session.client('s3')

#read the S3 inventory report, get the keys of files that are modified on sysdate-1   
dt=(datetime.now(timezone.utc) + timedelta(days=-1)).strftime('%Y-%m-%d') 
dtz=dt+'T00-00Z'
print('reading inventory report for', dtz)
inventory_bucket = 'xxx'
manifest_key='s3-bucket'+dtz+'/manifest.json'
manifest = json.load(s3.Object(inventory_bucket, manifest_key).get()['Body'])
df=pd.DataFrame()
for obj in manifest['files']:
        gzip_obj = s3.Object(bucket_name=inventory_bucket, key=obj['key'])
        print('csv obj:', gzip_obj)
        buffer = gzip.open(gzip_obj.get()["Body"], mode='rt')
        reader = pd.read_csv(buffer)
        reader.columns=['id','key','modified_date']
        print('converting csv obj to dataframe')
        df=df.append(reader[(reader.modified_date>dt)])
source_keys=list(df['key'])
s3_bucket_source='yyy'

#download the files to a tmp folder
local='/tmp/'
print("downloading from S3")
for k in source_keys:
  k_path=k.replace('/', '-')
  dest_pathname = os.path.join(local, k_path)
  if not os.path.exists(os.path.dirname(dest_pathname)):
      os.makedirs(os.path.dirname(dest_pathname))
      client.download_file(s3_bucket_source, k, dest_pathname)

#read the latest jsons from tmp folder
path_to_json =  os.path.expanduser('/tmp/') 
json_files = [pos_json for pos_json in os.listdir(path_to_json) if pos_json.endswith('.json')]

#loop through the jsons and normalize each file contents into df_table1, df_table2, df_table3
for index, js in enumerate(json_files):
    with open(os.path.join(path_to_json, js)) as json_file:
        print('processing file:', js)
        d=json.loads(json_file.read())
        v=d['key1'][0]['key2']
        if isinstance(v, list):
                for i, v2 in enumerate(v): 
                    df_table1, df_table2, df_table3 = normalizeJSON(d,i,v2['id']) 
                    #normalize is the custom function to split the nested json into relational tables 
        else:
            print('invalid json')
我使用S3库存报告从清单中获取最新修改文件的列表,将文件下载到tmp位置,并逐个读取它们,以完成我需要做的事情

您可以使用来并行下载JSON文件。 代码包含3个for块。您可以并行地执行其中的每一项。以下是如何为第一个应用程序执行此操作的示例:

第一个是:

变成:

def get_reader(obj):
    gzip_obj = s3.Object(bucket_name=inventory_bucket, key=obj['key'])
    print('csv obj:', gzip_obj)
    buffer = gzip.open(gzip_obj.get()["Body"], mode='rt')
    reader = pd.read_csv(buffer)
    reader.columns=['id','key','modified_date']
    print('converting csv obj to dataframe')
    return reader[(reader.modified_date>dt)]

num_of_workers = 4
df = pd.DataFrame()
with multiprocessing.Pool(num_of_workers) as p:
    results = p.map(get_reader, manifest['files'])

for result in results:
    df = df.append(result)

您可以对其他块执行相同的操作

请用代码更新问题。您在AWS基础设施上运行该问题,还是要在工作站上并行化?@Marcin我在AWS基础设施上运行代码,使用AWS胶水moment@Smile用代码段更新了问题最慢的部分是按顺序下载Python中的所有文件。一个简单的改进是使用gnu并行和awscli进行下载。另一个选项是使用gevent并行下载许多文件——当我尝试并行化上一个文件时,请参阅此代码以获得类似的示例,因为我得到以下错误
TypeError:map()从3到4个位置参数,但给出了5个位置参数
,感谢您的回复@pakallis。我修复了错误,并且我能够运行代码。代码中的第三个for块返回8个数据帧。但是map函数的输出是一个列表。因此,本例中的结果是8个数据帧的元组。有没有办法让map函数输出8个数据帧?
def get_reader(obj):
    gzip_obj = s3.Object(bucket_name=inventory_bucket, key=obj['key'])
    print('csv obj:', gzip_obj)
    buffer = gzip.open(gzip_obj.get()["Body"], mode='rt')
    reader = pd.read_csv(buffer)
    reader.columns=['id','key','modified_date']
    print('converting csv obj to dataframe')
    return reader[(reader.modified_date>dt)]

num_of_workers = 4
df = pd.DataFrame()
with multiprocessing.Pool(num_of_workers) as p:
    results = p.map(get_reader, manifest['files'])

for result in results:
    df = df.append(result)