はじめに
データを処理する上で、奇数行 or 偶数行にフラグを立てるという作業が発生した。
その際に高速化のために色々と試したのでメモっておく。
前提条件
前提条件として、以下のような10000行のデータフレームを持っているとする。
df = pd.DataFrame({'hoge':np.zeros(10000)}
df
hoge | |
---|---|
0 | 0.0 |
1 | 0.0 |
2 | 0.0 |
3 | 0.0 |
4 | 0.0 |
... | ... |
9995 | 0.0 |
9996 | 0.0 |
9997 | 0.0 |
9998 | 0.0 |
9999 | 0.0 |
このデータフレームに、'target_record'という奇数行 or 偶数行にフラグがたった以下のような列を追加する。
df['target_record'] = [1,0,1,0,1,...,0,1,0,1,0]
df
hoge | target_record | |
---|---|---|
0 | 0.0 | 1 |
1 | 0.0 | 0 |
2 | 0.0 | 1 |
3 | 0.0 | 0 |
4 | 0.0 | 1 |
... | ... | ... |
9995 | 0.0 | 0 |
9996 | 0.0 | 1 |
9997 | 0.0 | 0 |
9998 | 0.0 | 1 |
9999 | 0.0 | 0 |
このtarget_record列を作る際の時間を計算する。
ちなみに、処理時間は10000回計測した平均を求めた。
処理速度比較
loc + スライス
まずは、最も単純?な方法から
全レコードに0を代入した'target_record'列を追加し、loc + スライスで指定の行に1を代入するというもの。
df['target_record'] = 0
df.loc[0::2, 'target_record'] = 1 # 偶数行の場合は df.loc[1::2, 'target_record'] = 1
# 平均処理時間: 0.0009912237882614137 sec
ちなみにilocだと、
df['target_record'] = 0
df.iloc[0::2, 1] = 1
# 平均処理時間: 0.0009658613920211792 sec
若干locよりも早い?
np.zeros + スライス
locの処理速度が遅いのは有名な話なので、numpyで配列を作ってスライスで1を代入するというもの。
target_record = np.zeros(10000, dtype=int)
target_record[0::2] = 1 # 偶数行の場合は target_record[1::2] = 1
df['target_record'] = target_record
# 平均処理時間: 0.00035130116939544677 sec
処理時間が約1/3になった。
np.arange + 剰余
np.arange(10000)で0~9999の配列を作って、2で割った余りの値を代入するというもの。
target_record = np.arange(10000)
df['target_record'] = (target_record + 1) % 2 # 偶数行の場合は df['target_record'] = target_record % 2
# 平均処理時間: 0.00046031529903411863 sec
ちょっと工夫した処理だけどnp.zeros + スライスの方が0.0001秒速いらしい。
まとめ
奇数行 or 偶数行にフラグを立てるときは、np.zeros + スライスが最速?
ちなみに、処理ごとに時間を測ってみると、処理速度の差を分けたのは
剰余の計算かスライスで代入かという違いだった。
np.zerosとnp.arangeの処理速度にはほとんど差がなかった(1.0e-06秒だけzerosが速い)。