pandasの高速化
pandasの高速化¶
import numpy as np
import pandas as pd
from pandarallel import pandarallel
import swifter
ここではorderbookのデータをDataFrameに読み込みます。
raw_df = pd.read_pickle("btcusd_2020-07-08.pickle")
raw_df.head()
price | size | timestamp | side | |
---|---|---|---|---|
2020-07-08 22:00:05 | 9433.24 | 2.305583 | 2020-07-08 22:00:05 | bid |
2020-07-08 22:00:05 | 9434.32 | 1.000000 | 2020-07-08 22:00:05 | ask |
2020-07-08 22:00:05 | 9434.35 | 0.530100 | 2020-07-08 22:00:05 | ask |
2020-07-08 22:00:05 | 9434.90 | 0.015848 | 2020-07-08 22:00:05 | ask |
2020-07-08 22:00:05 | 9435.00 | 0.050000 | 2020-07-08 22:00:05 | ask |
depth=10の板情報からbid/askが最も近い価格(price)と枚数(size)を抽出します。
bid = (
raw_df.groupby("side")
.get_group("bid")
.groupby("timestamp")[["price", "size"]]
.max()
)
ask = (
raw_df.groupby("side")
.get_group("ask")
.groupby("timestamp")[["price", "size"]]
.min()
)
df = pd.concat([bid, ask], axis=1)
df.index.name = None
df.columns = "bid_price", "bid_size", "ask_price", "ask_size"
df.head()
bid_price | bid_size | ask_price | ask_size | |
---|---|---|---|---|
2020-07-08 22:00:05 | 9433.24 | 2.305583 | 9433.25 | 0.015848 |
2020-07-08 22:00:06 | 9433.24 | 2.305583 | 9433.25 | 0.015848 |
2020-07-08 22:00:07 | 9433.24 | 2.323611 | 9433.25 | 0.015848 |
2020-07-08 22:00:08 | 9433.61 | 1.700000 | 9433.62 | 0.015848 |
2020-07-08 22:00:09 | 9433.51 | 1.650000 | 9433.52 | 0.015848 |
サンプルとして、priceとsizeから仲値を算出する関数を作成します。
def get_mid_price(bid, bid_sz, ask, ask_sz):
try:
mid_price = ask + (ask_sz / (ask_sz + bid_sz) * (bid - ask))
except ZeroDivisionError:
mid_price = None
return mid_price
def get_mid_price_from_series(ser):
return get_mid_price(*ser)
vfunc = np.vectorize(get_mid_price)
作成した関数を各行に対して apply
メソッドで適用します。
%timeit df.apply(get_mid_price_from_series, axis=1)
59.8 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
numpy.vectorize はベクトル化した関数を定義します。引数には array-like なオブジェクトを渡します。
%timeit vfunc(*df.T.values)
2.46 ms ± 6.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
ちなみに
get_mid_price 関数の処理は関数化をしなくとも、演算子を使った式で算出できます
bid_, bid_sz_, ask_, ask_sz_ = df.T.values
%timeit ask_ + (ask_sz_ / (ask_sz_ + bid_sz_) * (bid_ - ask_))
17.1 µs ± 43.8 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
ちなみに
get_mid_price 関数内の処理は配列(numpy.ndarray)に対応しているため、この関数の引数にSeriesなどを渡せます
%timeit get_mid_price(*df.T.values)
132 µs ± 611 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
pandarallel.initialize()
INFO: Pandarallel will run on 1 workers.
INFO: Pandarallel will use Memory file system to transfer data between the main process and workers.
DataFrameから apply
メソッドの代わりに parallel_apply
メソッドを実行すると、関数を並列に適用します。
%timeit df.parallel_apply(get_mid_price_from_series, axis=1)
109 ms ± 4.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit df.swifter.apply(get_mid_price_from_series, axis=1)
115 ms ± 434 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)