pandas で DataFrame を行ごとに処理したいとき、iterrows を使う人がいます。しかし、iterrows は非常に遅いので、できるだけ避けるべきです。
iterrows が遅い理由
iterrows は 1 行ずつ Python のループで処理します。NumPy のベクトル化された演算に比べると、桁違いに遅くなります。
import pandas as pd
import numpy as np
import time
df = pd.DataFrame({
'a': np.random.rand(100000),
'b': np.random.rand(100000)
})
# iterrows で計算(遅い)
start = time.time()
result = []
for idx, row in df.iterrows():
result.append(row['a'] + row['b'])
df['c'] = result
print(f"iterrows: {time.time() - start:.2f}秒")
# ベクトル化で計算(速い)
start = time.time()
df['c'] = df['a'] + df['b']
print(f"ベクトル化: {time.time() - start:.4f}秒")
10 万行程度でも、iterrows は数秒かかりますが、ベクトル化なら一瞬で終わります。
なぜループを避けるべきか
pandas や NumPy の強みは、C 言語で実装された高速な配列演算です。Python のループを使うと、その恩恵を受けられません。
iterrows(ループ)
1 行ずつ Python オブジェクトに変換して処理。オーバーヘッドが大きい。
ベクトル化
配列全体を一度に処理。C 言語レベルの高速な演算。
代替手段 1:ベクトル化演算
算術演算や比較は、列に対して直接行えます。
# 加算
df['c'] = df['a'] + df['b']
# 条件分岐
df['flag'] = df['a'] > 0.5
# 複数条件
df['flag'] = (df['a'] > 0.5) & (df['b'] < 0.3)
代替手段 2:apply
行ごとに複雑な処理が必要な場合は apply を使います。iterrows よりは速いですが、ベクトル化には劣ります。
def process_row(row):
if row['a'] > 0.5:
return row['a'] * 2
else:
return row['b'] * 3
df['result'] = df.apply(process_row, axis=1)
代替手段 3:np.where
条件に応じて値を変える場合は np.where が高速です。
df['result'] = np.where(df['a'] > 0.5, df['a'] * 2, df['b'] * 3)
代替手段 4:np.select
複数の条件がある場合は np.select を使います。
conditions = [
df['a'] > 0.7,
df['a'] > 0.3,
]
choices = [
'high',
'medium',
]
df['category'] = np.select(conditions, choices, default='low')
iterrows を使ってもよい場面
とはいえ、iterrows が適切な場面もあります。
デバッグ時
少量のデータで処理内容を確認したいとき。
外部 API への送信
1 行ずつ外部サービスに送信する必要があるとき。ただし、バッチ処理が可能なら避けるべき。
パフォーマンスの目安
| 方法 | 相対速度 |
|---|---|
| ベクトル化 | 1倍(最速) |
| np.where | 1〜2倍 |
| apply | 10〜100倍遅い |
| iterrows | 100〜1000倍遅い |
数値は目安ですが、データが大きくなるほど差は広がります。
pandas を使うときは「ループを書かずに済む方法はないか」を常に考える習慣をつけましょう。ほとんどの処理はベクトル化か組み込みメソッドで実現できます。