組み込みでデータロガーみたいな製品を開発したときに、簡単にデータ操作できるようにPandasを使ったのですが、演算処理が重くなるところは .values
属性でNumpyデータに変換してから演算すると爆速だったのでメモ。
実験
データ定義
import pandas as pd
import numpy as np
data= np.random.rand(1000000)
s = pd.Series(data)
Jupyter上で、%%timeit
で実行時間を計測。
その1: 100万件のデータ積算
コード | 結果 | 速度比 |
---|---|---|
s.sum() | 6.38 ms ± 49.4 µs per loop | 1 |
s.values.sum() | 462 µs ± 8.75 µs per loop | 13.8 倍 |
data.sum() | 442 µs ± 12.9 µs per loop | 14.4 倍 |
.values
を入れるだけで13倍以上に速くなった。素のNumpyでの演算と遜色ないレベル。
その2: データ抽出演算
コード | 結果 | 速度比 |
---|---|---|
s[s > 0.5] | 12.3 ms ± 554 µs per loop | 1 |
s[s.values > 0.5] | 11.8 ms ± 23.4 µs per loop | 1.04 倍 |
s.values[s > 0.5] | 6.04 ms ± 28.7 µs per loop | 2.04 倍 |
s.values[s.values > 0.5] | 5.95 ms ± 26.1 µs per loop | 2.07 倍 |
data[data > 0.5] | 5.96 ms ± 45.7 µs per loop | 2.06 倍 |
.values
を入れるだけで2倍に速くなった。素のNumpyでの演算と同等レベル。
「その1」の結果と比べると大したことはない印象です。
ところが・・・
データ件数を1000件にして同じテストをしてみると、驚きの結果が。
data2 = np.random.rand(1000)
s2 = pd.Series(data2)
コード | 結果 | 速度比 |
---|---|---|
s2[s2 > 0.5] | 1.16 ms ± 12.1 µs per loop | 1 |
s2[s2.values > 0.5] | 1.06 ms ± 2.16 µs per loop | 1.09 倍 |
s2.values[s2 > 0.5] | 77.7 µs ± 1.5 µs per loop | 149 倍 |
s2.values[s2.values > 0.5] | 4.89 µs ± 172 ns per loop | 237 倍 |
data2[data2 > 0.5] | 3.81 µs ± 33.9 ns per loop | 304 倍 |
.values
を入れた演算だと200倍以上に速くなっています。素のNumpyの結果と比べると、Pandasの遅さが際立ちます。
データ件数が少なくなった分、Pandasでの演算オーバーヘッドの影響が大きくなるためと思われます。
まとめ
ここで実験した以外でも、Numpyで同じ演算ができる場合は、ほぼすべて .values
でNumpyデータを取得して演算したほうがかなり速くなります。
演算結果をPandasオブジェクトに戻す必要があるような場合には、その変換処理の時間だけ余計にかかりますが、それを考慮してもNumpyに変換したほうが速いことが多いでしょう。
時間的には大した差はないように見えますが、1msでも処理時間を削減したい組み込みシステムではこの差の積み重ねがかなり効いてきます。
とはいえ、同じ演算でもNaNの扱いなどPandasとNumpyで処理が違う場合もありますので注意が必要です。
自分も最初は何も考えずにNumpy演算に置き換えたら動かなくなってしまって苦い経験をしました。やはりPandasでの演算の方が何かと便利にできています。この便利さがPandasの遅さの原因でもあります。
また、Seriesならまだよいですが、DataFrameになると記述が冗長になり可読性は低下しますし、バグの原因になりかねません。
TPOに応じて使い分けることになりそうです。