LoginSignup
3
4

More than 5 years have passed since last update.

格納先がS3のgzip圧縮ファイルをpandasを用いて読み書き

Last updated at Posted at 2019-02-27

背景

AWS Lambdaのメモリ制限が3GBに倍増されて久しいが、昨年10月にtimeout制限も5分から15分になった。
AWSから話を聞くところによると、この変更はAWS GlueとLambdaの空白地帯を埋めるためらしい。
少し突っ込んで説明をすると、データ変換にはGlueを用いることがよくある。しかし、これは1回の最低課金時間が10分と比較的長く、数秒~数分で終わってしまうような軽いデータの変換では無駄な料金を払うことになってしまう。
そのため、比較的特性の近いLambdaを代替として使うことも多かったが、こちらは実行時間制限が5分しかなく、実行時間が5~10分のジョブに関しては結局Glueを使用するしかなかったのですが、
今回の制限変更を経てこの空白を埋めることができたわけです。
(処理能力の差とか細かい事を言うといろいろあるのですが、本題でないので割愛します)
すなわち、Lambdaを比較的軽いデータの変換リソースとして使ってもらう狙いがあるようです。

※今年の1月22日にPythonシェルジョブというタイプという物ができ、界隈の事情がさらに変わっていますが、その話は置いておきます。

さて、そんな時にS3へアップされるデータを利用しやすい形式に変換して再度保存する機能を組むことになったので
私は(いろいろ検討した結果)このAWSの狙いにどっぷりと乗っかることにしてLambdaを用いた変換を行うことにしました。

要件

・最大1GB程度になる生のCSVデータがgzip圧縮された状態で定期的にS3にアップされてくる
・このファイルを読み込んで、必要な処理をしたのち再度gzip化したcsvファイルをS3に保存する
・アップしたファイルはすぐに変換処理に回して変換後からすぐに使用したいので比較的高速なこと
・Lambdaを使用する
 →ディスクが512MBしかないのでオンメモリ上でさばく必要がある
 →メモリの制限があるので、じゃぶじゃぶと変数を使えない

ということで、gzipをわざわざディスクに落としてきたりせず、またメモリ上でgzipを解凍したりせずに、直接データ読み込んで、そのまま必要な編集をして、編集後のデータもどこかに吐き出したりせず、直接S3にgzip化して書き出すという処理をしたいわけです。これをpandasとs3fsを利用して実現していきます。

準備

lambdaで実行する場合はpandasとs3fsを実行できるようにモジュールの実ファイルを用意する。
下記コマンドを打って、生成されたファイル群をコードと一緒にあげる。

pip install pandas -t ./
pip install s3fs -t ./

S3に格納されたCSV.gzをpandasに読み込む

pandasにはread_csvというcsvファイルを読み込むメソッドがあります。
こいつが何とも強力で、S3にあるパスを直接指定することができます。(Lambdaからのアクセス権を設定する必要があります)
更に、対応する圧縮形式であれば、拡張子から形式を自動判別して直接読むことができます。
もちろん、その中にgzipが含まれるので、読み込みはこのメソッドに任せればOKです。
なお、.gz以外の拡張子のgzipはcompressionパラメタに'gzip'を指定することで読み込めるようです。

import pandas as pd
df = pd.read_csv( 's3://bucket_name/load/csv.gz' )

pandasに格納されているデータをgz圧縮した状態でS3に格納する

※2019/03/14修正しました
以前のコードではどうやら、見た目上はファイルが作成されているのですが、破損ファイルが保存されるようです。

gzip化するにはpythonの標準ライブラリのgzipを使用します。

import pandas as pd         
import gzip

buf = io.BytesIO()
with gzip.GzipFile(mode='w', fileobj=buf) as gz_file:
    data.to_csv(gz_file)
s3 = boto3.client('s3', region_name='region', aws_access_key_id='access key', aws_secret_access_key='secret key')
s3.put_object(Bucket='bucketName', Key='outpath', Body=buf.getvalue())

まとめ

想像していたよりは簡単に行きました。
しかし、500MBを越えるデータをLambdaで回すと、すぐにメモリ不足に陥るのでメモリの量か、ディスクの量がもっと欲しくなります。
GlueでPythonを動かせるようになったので、今後はそちらの利用も選択肢の一つに入れるといいかもしれませんね。

参照

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_csv.html
https://s3fs.readthedocs.io/en/latest/

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4