フリーランチ食べたい

機械学習/データ解析/フロントエンド/バックエンド/インフラ

Pandasで行うデータ処理を100倍高速にするOut-of-CoreフレームワークVaex

TL;DR

  • アウトオブコア、かつマルチコアでデータ処理を行えるVaexの紹介です。
  • string関係のメソッドで平均して100倍以上の高速化が確認できました。(作者のベンチマークだと最大1000倍)
  • 文字列処理以外でも数倍~数十倍の高速化が行えそうです。
  • この記事では性能の比較のみ行い、解説記事は別で書こうと思います。

f:id:ikedaosushi:20190414003238p:plain

pandasより1000倍早いフレームワーク?

今週、興味深い記事を読みました。重要な部分だけ抜き出すと次のような内容です。

  • Vaexの最近のアップデートでの文字列処理が超早くなった
  • 32コアだとpandasと比べて1000倍早い

towardsdatascience.com

1000倍って本当なの?って感じですよね。そもそも自分はVaex自体を知らなかったので調べてみました。

ちなみに調べていて気づいたのですが、この記事の著者はVaexの作者なんですよね。 疑っているわけではないですが、第三者として性能について検証してみたいと思いました。

Vaex

github.com

Vaexとは、GitHubページを見ると「遅延評価でき、Pandasライクで、アウトオブコアな、データフレームのライブラリ」と書いてあります。

Vaex is a python library for lazy Out-of-Core DataFrames (similar to Pandas)

Vaexには次のような特徴があります。

  • 高パフォーマンス: 大規模データに対して高速に動作します。
  • 遅延評価/仮想関数のサポート
  • 高メモリ効率性: Pandasのメソッドのいくつかはディープコピーを伴います。
  • 大規模データの可視化: アウトオブコアでプロットする機能があります
  • ユーザーフレンドリーなAPI
  • 適切なモジュール化: パッケージが適切に分割されており、リーンな開発が行える。
  • Jupyterのインテグレーションあり
  • 機械学習のモジュールもあり

2014年から最初のBlogの著者であるMaarten Breddelsを中心に開発されていたみたいです。

f:id:ikedaosushi:20190413131216p:plain

データ解析やトランスフォームを行う際には、Pandasを使うのが一般的だと思いますが、 Pandasの作者であるWes McKinneyは「私がPandasについて嫌っている10個の事」という記事でPandasに対しての後悔を語っています。その中でも「10. 遅延評価のサポートがない事」「11. 大規模データセットに対しての低パフォーマンス」を解決しているのがVaexです。

wesmckinney.com

検証環境

  • マシン: AWS EC2: c5.9xlarge(コア36個, メモリ72GiB)
  • OS:
$ cat /etc/system-release
Amazon Linux release 2 (Karoo)
  • Python:
$ python -V
Python 3.7.2
  • Packages
for m in [vaex, dask, pd]:
    print(m.__name__, "->", m.__version__)
# vaex -> 1.0.0-beta.6
# dask -> 1.2.0
# pandas -> 0.24.2

性能を調査してみる

それでは本当にPandas/Daskより早いのか比較してみましょう。 ブログ記事に書かれていた文字列処理で試してみました。

データは1000万の文字列を生成して用いました。

# メインのライブラリ
import vaex
import pandas as pd
import dask.dataframe as dd
import numpy as np

# その他のライブラリ
import multiprocessing
from tqdm import tqdm
import time

# データを生成
n = 10**7 # 1000万
s = np.arange(n).astype(str) # stringにする
vx_df = vaex.from_arrays(s=s)

# 一旦hdf5で書き出す
# オンメモリだと性能がでないようです。
file = './data/test.hdf5'
vx_df.export(file, progress=True, shuffle=True)

# dfを準備
vx_df = vaex.open(file)
vx_df.executor.buffer_size = len(vx_df) // (multiprocessing.cpu_count() * 2)
pd_df = vx_df.to_pandas_df()
dd_df = dd.from_pandas(pd_df, npartitions=4)

# 計測用関数
def timeit(expr, n, scope):
    times = []
    for i in range(n):
        s = time.time()
        eval(expr, scope)
        e = time.time()
        times.append(e - s)

    return times

# 試すメソッドの準備
methods = {
    'capitalize': 'df.s.str.capitalize()',
    'cat': 'df.s.str.cat(df.s)',
    'contains': 'df.s.str.contains("9", regex=False)',
    'contains(regex)': 'df.s.str.contains("9", regex=True)',
    'count': 'df.s.str.count("9")',
    'endswith': 'df.s.str.endswith("9")',
...

# 計測
n = 3

performances = {}
for name, expr in tqdm(methods.items()):
    # daskはcompute()で評価
    dask_expr = expr + ".compute()"
    
    # nop() はevaluateして結果を捨てるベンチマーク用のメソッド
    vaex_expr = expr + ".nop()" 

    t_pd = timeit(expr, n, scope={'df': pd_df})
    t_dd = timeit(dask_expr, n, scope={'df': dd_df})
    t_vx = timeit(vaex_expr, n, scope={'df': vx_df})
    
    performance = {
        'pandas': t_pd,
        'dask': t_dd,
        'vaex': t_vx
    }
    performances[name] = performance

結果

それでは結果を見てみましょう。メソッドごとにそれぞれのライブラリが平均何秒かかったのがをグラフにしたのが以下です。

f:id:ikedaosushi:20190413202946p:plain

めちゃくちゃ早い! Vaexの棒グラフが小さすぎてほとんど見えないのですが、左の方に「ちょこん」としているのがVaexです。

平均すると134倍早いという結果になりました。1000倍はいかないにしても相当早いですね。

文字列以外だとどうか

せっかくなので文字列処理以外も試したかったので簡単な四則演算をしてみました。

f:id:ikedaosushi:20190413204038p:plain

これも文字列処理まではいきませんが平均22倍pandasより高速という結果になりました。 本当にすごいですね。

欠点・微妙なポイント

これだけ書くともうPandasいらないじゃん、というなりそうですが、少し触ってみてわかった欠点・微妙なポイントも書いておきます。

  • APIの不足
    • Pandasに比べるとまだまだ足りなさそうです。Pandasが積み重ねてきた歴史を考えると当然ですね。
  • 高速化されるパターンが限定されそう
    • これはまだVaexの知識があまりないからかもしれません。
    • ただ試してみた限りファイル形式が hdf形式を前提にしているexampleが多く、実際hdf形式以外だと速度が早くならなかったりしました。

でも全体を通じて2015年から継続的に開発されていることもあり、手が行き届いたライブラリだと感じました。

Pandasを置き換える?

Pandasとの比較については冒頭のBlog記事にも書かれていたので引用します。

Pandas will be around forever, its flexibility is unparalleled, and for a big part responsible for the Python’s popularity in data science. However, the Python community should have good answers when datasets become too large to handle. Dask.dataframe tries to attack large datasets by building on top of Pandas, but inherits its issues.

ざっくり翻訳すると、「Pandasは便利で当分の間はなくなることはないし、データ解析という分野におけるPythonの人気を支えてきた存在だけど、 (Wes McKinneyが書いていたように)大規模データセットに対するパフォーマンスなど問題を解決しなければならないDaskはその問題に立ち向かってきたが、問題自体を継承してしまっている」というようなことが書いてあります。

言っていることはとても理解できます。Daskは素晴らしいと思いますし、実際、自分はプロダクション環境でDaskを使っています。しかし、Pandasとのラッパーという立ち位置を守ることが、速度面/性能面の根本的な問題の解決の妨げになっていると感じます。なので、Vaexを開発する意義や目的は明確にあり、単なる「俺が考えた最強のライブラリ」でないと自分は考えています。

まとめ

  • Out-of-CoreフレームワークVaexの紹介と性能検証を行いました。
  • ちょっと調べてみるつもりが、予想以上の性能と面白さでのめりこんで調べてしまいました。
  • Vaexの入門記事も書きたいと思います。引き続き要チェックなフレームワークですね!

※追記: 書きました blog.ikedaosushi.com

  • また計測に使ったコードは↓のGitHubにおいてあります。

github.com