はじめに:バックテストが終わらない夜
Pythonで株の自動売買(スイングトレード)モデルを作り、過去10年分の株価データ(約2000銘柄 × 2500日 = 500万行)でバックテストを回そうとしました。
「よし、1行ずつループしてシグナルを判定しよう!」
私は何も疑わずに、Pandasの伝家の宝刀 iterrows() を抜きました。
# ❌ 初心者がやりがちな絶望コード
for index, row in df.iterrows():
if row['close'] > row['sma_25']:
df.at[index, 'signal'] = 1
else:
df.at[index, 'signal'] = 0
スクリプトを実行。……終わらない。
プログレスバーは1秒に1%も進みません。計算が終わるまでに推定4時間かかると表示されました。
「Pythonってこんなに遅いの…?」と絶望した夜の話です。
なぜ iterrows() は罪なのか
Pandasのデータフレームは、内部的にはC言語の配列(NumPy)でメモリ上に綺麗に並んでいます。
しかし iterrows() を使うと、Pythonは「1行ごとにCの世界からPythonの世界へデータを変換し、Pythonの遅いループで処理し、また戻す」というオーバーヘッドを500万回繰り返します。これは東京から大阪まで、歩いて荷物を1個ずつ運ぶようなものです。
救世主:「ベクトル化(Vectorization)」
Pandas(NumPy)の真の力は**「ベクトル化」**にあります。ループを回さず、列全体に対して一気に計算を適用するのです。
# ✅ ベクトル化を使った神コード
df['signal'] = np.where(df['close'] > df['sma_25'], 1, 0)
この1行に書き換えてスクリプトを実行しました。
処理時間は 0.2秒 になりました。
4時間かかっていた処理が、一瞬で終わったのです。
Pandasループの「やってはいけない」ランキング
データ分析やシストレで処理が遅いと感じたら、以下の順に改善を検討してください。
-
【最悪】
iterrows():絶対に避けるべき。遅さの極み。 -
【マシ】
itertuples():どうしてもループが必要ならこっち。数倍速い。 -
【普通】
apply():便利だが、内部的にはループしているので巨大データには向かない。 -
【最強】 ベクトル化(NumPy関数):条件分岐には
np.whereやnp.selectを使う。圧倒的に最速。
Pythonは「遅い言語」ではありません。「遅い書き方ができてしまう言語」なだけです。データフレームをループで回そうとした時、それは「あ、自分は今、重罪を犯そうとしているな」と気づくためのサインです。