Vaexで移動平均を計算

どりらんです。この記事はfintalk Advent Calendar 2019の11日目です。
ここのボスになんか書けと言われたので書きました。

Vaexとは

pandasのようなテーブルデータ(DataFrame)を扱うライブラリです。Out-of-Core(メモリにのらない大きなデータ)を遅延処理で計算できるのが特徴です。

pandasの1000倍速いと言われており、シャア少佐もびっくりです。気になったのでちょろっと試してみました。金融ネタということで、今回は移動平均を算出してみます。

インストールなど

公式ドキュメントに書いてあるので割愛します。

サンプルデータ

時系列データを用意するのが大変なので、今回は乱数で代用します。

In [1]:
import numpy as np
import pandas as pd
import vaex

t = np.arange("1900-01-01", "2019-12-25", dtype=np.datetime64)
y = np.random.rand(len(t))

比較用にpandasのDataFrameを作成します。

In [2]:
df_pd = pd.DataFrame({"t": t, "y": y})
df_pd.head()
Out[2]:
t y
0 1900-01-01 0.432008
1 1900-01-02 0.028274
2 1900-01-03 0.590348
3 1900-01-04 0.294353
4 1900-01-05 0.554144

Vaexでは引数自体がカラム名となり、データを渡す形式のようです。

In [3]:
df_vaex = vaex.from_arrays(t=t, y=y)
df_vaex.head()
Out[3]:
# t y
01900-01-010.432008
11900-01-020.0282736
21900-01-030.590348
31900-01-040.294353
41900-01-050.554144
51900-01-060.0938365
61900-01-070.701837
71900-01-080.484192
81900-01-090.874886
91900-01-100.480992

まずは、pandasで5日間の移動平均を算出してみます。

In [4]:
%timeit df_pd.rolling(5).mean()
1.37 ms ± 109 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Vaexにはrollingに相当する関数がなさそうなので、groupbyメソッドで代用します。

In [5]:
%timeit df_vaex.groupby(vaex.BinnerTime(df_vaex["t"], "5D")).agg({"y": "mean"})
18.7 ms ± 4.74 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

1000倍速くなるどころか、10倍遅くなってますね...このケースでは関係ないかもしれませんが、ファイルに書き出したほうがメモリの負担が少ないらしいので、HDF5にから読み込んで実行してみます。

In [6]:
df_vaex.export_hdf5("price.hdf5")
df_vaex_hdf5 = vaex.open("price.hdf5")
In [7]:
%timeit df_vaex_hdf5.groupby(vaex.BinnerTime(df_vaex_hdf5["t"], "5D")).agg({"y": "mean"})
18.9 ms ± 5.12 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

この程度のデータ量だと、あまり意味がないかもしれません。
文字列の処理などは高速化するようですが、datetime型のデータがどこまで高速化されるかはよくわかってません。

当たり前ですが、pandasのほうが機能が豊富であり。同じことをOut-of-Core処理したいのであればDaskを使うのがよさそうです。

Vaexは公式ドキュメントのAPIリファレンスが不十分で、今回の時系列データのリサンプル方法を見つけるのも大変でした(週や月などの決まった期間は載っていました)。今後に期待したいところです。