新しいCSVファイルにアップロードするpandas DataFrameがあります。問題は、s3に転送する前にファイルをローカルに保存したくないことです。データフレームをs3に直接書き込むto_csvのような方法はありますか? boto3を使用しています。
これは私がこれまでに持っているものです。
import boto3
s3 = boto3.client('s3', aws_access_key_id='key', aws_secret_access_key='secret_key')
read_file = s3.get_object(Bucket, Key)
df = pd.read_csv(read_file['Body'])
# Make alterations to DataFrame
# Then export DataFrame to CSV through direct transfer to s3
次を使用できます。
from io import StringIO # python3; python2: BytesIO
import boto3
csv_buffer = StringIO()
df.to_csv(csv_buffer)
s3_resource = boto3.resource('s3')
s3_resource.Object(bucket, 'df.csv').put(Body=csv_buffer.getvalue())
s3fs が好きです。これにより、ローカルファイルシステムのように(ほぼ)s3を使用できます。
あなたはこれを行うことができます:
import s3fs
bytes_to_write = df.to_csv(None).encode()
fs = s3fs.S3FileSystem(key=key, secret=secret)
with fs.open('s3://bucket/path/to/file.csv', 'wb') as f:
f.write(bytes_to_write)
s3fs
は、ファイルを開くrb
およびwb
モードのみをサポートしているため、これをbytes_to_write
で行いました。
これは最新の回答です:
import s3fs
s3 = s3fs.S3FileSystem(anon=False)
# Use 'w' for py3, 'wb' for py2
with s3.open('<bucket-name>/<filename>.csv','w') as f:
df.to_csv(f)
StringIOの問題は、メモリを使い果たしてしまうことです。この方法では、ファイルを文字列に変換してからs3に書き込むのではなく、ファイルをs3にストリーミングします。 pandasデータフレームとその文字列コピーをメモリに保持することは非常に効率が悪いようです。
Ec2インスタントで作業している場合、s3への書き込みを可能にするIAMロールを与えることができるため、資格情報を直接渡す必要はありません。ただし、S3FileSystem()
関数に資格情報を渡すことでバケットに接続することもできます。ドキュメントを参照してください: https://s3fs.readthedocs.io/en/latest/
S3パスを直接使用できます。 Pandas 0.24.1 を使用しています
In [1]: import pandas as pd
In [2]: df = pd.DataFrame( [ [1, 1, 1], [2, 2, 2] ], columns=['a', 'b', 'c'])
In [3]: df
Out[3]:
a b c
0 1 1 1
1 2 2 2
In [4]: df.to_csv('s3://experimental/playground/temp_csv/dummy.csv', index=False)
In [5]: pd.__version__
Out[5]: '0.24.1'
In [6]: new_df = pd.read_csv('s3://experimental/playground/temp_csv/dummy.csv')
In [7]: new_df
Out[7]:
a b c
0 1 1 1
1 2 2 2
S3ファイル処理
pandasは、S3接続の処理にs3fsを使用するようになりました。これによりコードが破損することはありません。ただし、s3fsは必須の依存関係ではないため、以前のバージョンのpandasのbotoのように、s3fsを個別にインストールする必要があります。 GH11915 。
None
を to_csv()
の最初の引数として渡すと、データは文字列として返されます。そこからS3に一度にアップロードするのは簡単なステップです。
StringIO
オブジェクトをto_csv()
に渡すことも可能ですが、文字列を使用する方が簡単です。
バケットs3から2列のcsvを読み取り、ファイルcsvのコンテンツをpandasデータフレームに入れました。
例:
config.json
{
"credential": {
"access_key":"xxxxxx",
"secret_key":"xxxxxx"
}
,
"s3":{
"bucket":"mybucket",
"key":"csv/user.csv"
}
}
cls_config.json
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import json
class cls_config(object):
def __init__(self,filename):
self.filename = filename
def getConfig(self):
fileName = os.path.join(os.path.dirname(__file__), self.filename)
with open(fileName) as f:
config = json.load(f)
return config
cls_pandas.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pandas as pd
import io
class cls_pandas(object):
def __init__(self):
pass
def read(self,stream):
df = pd.read_csv(io.StringIO(stream), sep = ",")
return df
cls_s3.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import boto3
import json
class cls_s3(object):
def __init__(self,access_key,secret_key):
self.s3 = boto3.client('s3', aws_access_key_id=access_key, aws_secret_access_key=secret_key)
def getObject(self,bucket,key):
read_file = self.s3.get_object(Bucket=bucket, Key=key)
body = read_file['Body'].read().decode('utf-8')
return body
test.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from cls_config import *
from cls_s3 import *
from cls_pandas import *
class test(object):
def __init__(self):
self.conf = cls_config('config.json')
def process(self):
conf = self.conf.getConfig()
bucket = conf['s3']['bucket']
key = conf['s3']['key']
access_key = conf['credential']['access_key']
secret_key = conf['credential']['secret_key']
s3 = cls_s3(access_key,secret_key)
ob = s3.getObject(bucket,key)
pa = cls_pandas()
df = pa.read(ob)
print df
if __== '__main__':
test = test()
test.process()