10
11

More than 5 years have passed since last update.

PandasのDataFrameをMessagePackに簡単に読み書きできるようになっていた件

Last updated at Posted at 2018-11-20

大きいCSVデータを扱うときに、一時的に中間ファイルに保存して高速にアクセスしたいケースがある。そんなときMessagePackはかなり有効なシリアライザーなのだが、いつの間にかPandasでサポートされていたので確かめてみた。

結論:CSVよりも相当速くなった

読み書きのやり方

DataFrame→MessagePackへの書き出し

df.to_msgpack()で書き出せる。まだ実験段階なので仕様が変わるかもしれないが、「path_or_buf」の引数がNoneならMessagePackの文字列として返され、引数のパスを指定すればファイルとして保存される。

また、compressの引数に圧縮形式を指定すると圧縮をかけてくれる。現在「zlib」と「blosc」形式がサポートされている。

import pandas as pd

# DataFrameからMessagePack(path_or_buf=Noneなら文字列として返される)
msgpack_str = df.to_msgpack(None)

# DataFrameからMessagePackのファイルに保存
df.to_msgpack("df.bin")

# DataFrameからMessagePackのファイルに保存(zlibで圧縮)
df.to_msgpack("df_zlib.bin", compress="zlib")

詳細:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_msgpack.html

MessagePack→DataFrameへの読み込み

csvと同じ要領で、pd.read_msgpack()で読み込める。まだ実験段階なので仕様が変わるかもしれないが、引数のpathがファイルパスならファイルから読み込み、MessagePackの文字列ならパースをするらしい(ここ変わりそうな気がする)。

# MessagePackの文字列からDataFrame
df_read = pd.read_msgpack(msgpack_str)

# MessagePackのファイルからDataFrameに読み込み
df_read = pd.read_msgpack("df.bin")

# zlibで圧縮したMessagePackのファイルからDataFrameに読み込み(圧縮形式は指定しなくてよい)
df_read = pd.read_msgpack("df_zlib.bin")

事前に圧縮をしていても勝手に形式を判断してくれる。

詳細:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_msgpack.html

速度、ファイルサイズの比較

以下の4ケースで「読み、書きにかかる時間」「ファイルサイズ」を測定する。

  1. CSVで読み書き
  2. to_pickle / read_pickleで読み書き
  3. MessagePack(圧縮なし)で読み書き
  4. MessagePack(zlib圧縮)で読み書き

環境:Python3.6.4, pandas:0.23.4, zlib:1.2.11

対称ファイル:KaggleのThe Movies Datasetのratings.csv(解凍済み676MB)で実験する。ディスクアクセスのオーバーヘッドを減らすため読み書きはSSD環境で行う。

bloscフォーマットはモジュールをインストールしてもエラーが出て圧縮できなかったので省略。

1.CSVで読み書き

import pandas as pd
import time

source_dir = "F:/"

def read_csv():
    return pd.read_csv(source_dir+"ratings.csv")

def test_csv():
    df = read_csv()
    start_time = time.time()
    df.to_csv(source_dir+"ratings_out.csv")
    print("write csv [s]", time.time() - start_time)

    start_time = time.time()
    df = pd.read_csv(source_dir+"ratings_out.csv")
    print("read csv [s]", time.time() - start_time)

write csv [s] 141.75099992752075
read csv [s] 21.151185989379883
ファイルサイズ:889MB

書き込みで2分オーバー。正直CSVのままやり取りするのはつらい。ファイルサイズが増えているのはエンコードがUTF-8に変わったせいだろう。

2. to_pickle / read_pickleで読み書き

def test_pickle():
    df = read_csv()
    start_time = time.time()
    df.to_pickle(source_dir+"pickle.bin")
    print("write pickle [s]", time.time() - start_time)

    start_time = time.time()
    df = pd.read_pickle(source_dir+"pickle.bin")
    print("read pickle [s]", time.time() - start_time)

write pickle [s] 3.3524258136749268
read pickle [s] 1.1086409091949463
ファイルサイズ:794MB

CSVに比べると相当速い。しかし、PythonのPickleには任意コードを実行されるという脆弱性がある。使うのは簡単だけど、そこを知った上で使うべき。

3. MessagePack(圧縮なし)で読み書き

def test_raw_msgpack():
    df = read_csv()
    start_time = time.time()
    df.to_msgpack(source_dir+"msgpack.bin")
    print("write msgpack [s]", time.time() - start_time)

    start_time = time.time()
    df = pd.read_msgpack(source_dir+"msgpack.bin")
    print("read msgpack [s]", time.time() - start_time)

write msgpack [s] 3.6599645614624023
read msgpack [s] 2.516319513320923
ファイルサイズ:794MB

Pickleよりは若干遅いものの、こちらはMessagePackのライブラリがある言語で読み込むことができる。ファイルサイズが一緒なので、内部的なシリアライズ方式はPickleと似ているのかもしれない。しかし、脆弱性の点からはMessagePackのほうが安全な(はず)。

4. MessagePack(zlib圧縮)で読み書き

def test_zlib_msgpack():
    df = read_csv()
    start_time = time.time()
    df.to_msgpack(source_dir+"msgpack_zlib.bin", compress="zlib")
    print("write msgpack-zlib [s]", time.time() - start_time)

    start_time = time.time()
    df = pd.read_msgpack(source_dir+"msgpack_zlib.bin")
    print("read msgpack-zlib [s]", time.time() - start_time)

write msgpack-zlib [s] 29.289719343185425
read msgpack-zlib [s] 3.1884050369262695
ファイルサイズ:124MB

Zlib圧縮をかけるため書き込みに時間がかかるが、読み込みはそこまで遅くなく、非対称な圧縮なのでこれで十分ではないだろうか。データ分析の場合、書き込みよりも読み込みの頻度のほうが多いと思われるので。

まとめ

形式 書き込み[s] 読み込み[s] 容量[MB]
CSV 141.8 21.2 889
Pickle 3.4 1.1 794
MessagePack 3.7 2.5 794
MessagePack+zlib 29.3 3.2 124

速さ重視ならPickleだが、脆弱性があるのでオレオレシリアライズ専用かもしれない。その点からはMessagePackは安心して使えて、Redisでもサポートされている1。事実MessagePackとPickleはほとんど差がない(特に容量が)。容量削りたければMessagePack+zlibでほぼFAでは?という感じはする。他に無圧縮MessagePackでかけて、別の軽い圧縮(LZ4とか)をかけるのでも良さそうな気はする。とりあえずCSVで直に処理するのはない


  1. MessagePackの公式ページより 

10
11
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
10
11