pythonのforループは遅い?
pythonはデータの処理になにかと便利なので重宝していますが、ループ(for)の処理は一般的に遅いと言われています。(このためベクターで処理するのが良いらしい)
とはいえ、今まで実感としてはなかったんですが、ちょっとした処理をforループとnumpyで比較してみたら、桁違いだったのでメモにまとめます。
処理内容
処理したのは年間の風データ。1時間毎の風向(角度)のデータです。
- 風向(0~360°)
- 1時間単位で1年分(8760h)
これを16方位、22.5°刻みの角度へ集約します。
forループ
まず、forループを使って次のように処理しました。8760h×16方位のループです。
theta = 360/16
for i in range(8760):
for j in range(16):
angle = theta*j
if(j==0):
if ((360 - theta/2 < winddirs[i]) or (winddirs[i] <= angle + theta/2)):
winddirs[i] = 0.0
break
else:
if ((angle - theta/2 < winddirs[i]) and (winddirs[i] <= angle + theta/2)):
winddirs[i] = angle
break
ループ処理としてありがちなパターンですが、これがひたすら遅い。恐ろしく遅い。処理時間は約240秒。処理がなかなか終わらないのでフリーズしたのかと思うほどです。
2021/11/10追記
いま同じようなコードを書いたら0.5秒ぐらいで終了してしまって、以前の240秒は何だったんだろう?ってなっている。PCを更新しているので、その影響?それにしても処理速度が違いすぎるので何か間違えていたのかも?
ちなみに同じコードをNumpyに書き直すと計測不能なほど速い。やはりforループとNumpyの速度差は大きいのは確かなようです。
Numpy
そして、NumPyで書き直したのが次のコード。where()を使って8760h分の値をまとめて一気に条件判定して値を更新します。
theta = 360/16
for j in range(16):
angle = theta*j
if(j==0):
min_angle = 360 - theta/2
max_angle = theta/2
winddirs = np.where((min_angle < winddirs)|(winddirs <= max_angle), 0.0, winddirs)
else:
min_angle = angle - theta/2
max_angle = angle + theta/2
winddirs = np.where((min_angle < winddirs) & (winddirs <= max_angle), angle, winddirs)
ステップ数はそれほど変わりませんが、こちらは約0.5秒、なんと500倍近い速度アップです。ループの処理が大幅に減った(16×8760hから16個)も効いているようですが、それにしてもベクトルをまとめて処理すると速い。
まとめ
ということで、一般に言われているようにループ処理が遅いのは確かなようです。
大量のデータはループを使わず、Numpyなどでまとめて処理すると高速に処理できます。
動作環境
以下の環境で動作を確認しています。
Windows10 Pro(64bit, 1809)
Python3.7.3