こんにちは。
最近、少し重めのデータを扱うことがありまして、Pandasで読み込もうとしたところ、メモリエラーを食いました。良い機会なので大きめのcsvファイルを扱う時に行うことを自分用メモとしてまとめておこうと思います。
方法一覧
大きめのファイルを扱う観点はいくつかあると思うのですが、今回はメモリ使用量に着目してやっていきます。
内部の処理が詳細にどうなっているかまでは追えていませんが*1、ファイルを読み込んでいる途中でメモリエラーになることもあり、それを回避する方法として、次のパラメーターが有効だったので、それについて説明していきます。
- usecols
- chunk_size
- dtype
- low_memory
本当はmemory_profilerとかでメモリ使用量の比較をしようと思ったのですが、どうやら内部で使われている関数の中までは追いかけて、メモリ使用量を出してくれるわけではないっぽく、今回のケースでは比較することができませんでした。
usecols
: 読み込むカラムを指定する
まずは、これができるならやったほうがいいusecols
のパラメーターです。
data = pd.read_csv('./data.csv', usecols=['columnA', 'columnB', 'columnC'])
読み込み時に、読み込むカラムを限定できるので、当然使用するメモリの量も少ないです。
chunk_size
: 少しずつ読み込む
read_csvをした時に、読み込んでいる途中で落ちるパターンの時に有効です。メモリに乗り切らない物を分割して扱うという意味でもいいですが、ファイルサイズ的に「メモリに乗っかると思うんだけどなぁ」みたいな時は、chunk_sizeを分割して読み込んで、concatしてあげると、通常のpandasと同じく扱うことができます*2。
data_reader = pd.read_csv('./data.csv', chunksize=1000) data = pd.concat((r for r in data_reader), ignore_index=True)
dtype
: 型を事前に指定する
読み込んだ後に行われるメモリ削減の話でもよく出てくる型指定ですが、メモリ削減にも効果があります。例えば、以下のKaggleのデータを具体例に、やってみます。
data = pd.read_csv('./data.csv', dtype={'id': np.int64, 'url': np.object, 'region': np.object, 'region_url': np.object, 'price': np.int64, 'year': np.float16, 'manufacturer': np.object, 'model': np.object, 'condition': np.object, 'cylinders': np.object, 'fuel': np.object, 'odometer': np.float32, 'title_status': np.object, 'transmission': np.object, 'vin': np.object, 'drive': np.object, 'size': np.object, 'type': np.object, 'paint_color': np.object, 'image_url': np.object, 'description': np.object, 'county': np.float64, 'state': np.object, 'lat': np.float16, 'long': np.float16})
このようにdtype
を指定することによって、指定しなかった場合であれば大きめの型(float32で十分なものでも、float64が使われるなど)で指定され、メモリが余計に使われるところも、節約することができます。また、Pandasは型を推論する時に、列のデータを全部読み込むなどを内部的にしているみたいなのですが、それも無くなり、処理速度が早くなります。
low_memory
: 型の推論を一部のデータでやる
ただ、事前に型を指定するのが難しい場合で、列の型が一定であることが事前に保証される場合には、 low_memory
を使うことでメモリを節約できます。
data = pd.read_csv('./data.csv', low_memory=True)
まとめ
これらの組み合わせとかで対応することで、よりメモリ節約をして行く感じかなぁと。他にオススメの方法があれば教えて欲しいっす。
それでは。