iterrows() 在大数据量下极慢,因其每次返回 Pandas Series,触发大量 Python 对象创建和类型推断,无法利用 NumPy 向量化;10 万行可比向量化操作慢 50–100 倍。

为什么 iterrows() 在大数据量下会慢得明显
因为 iterrows() 每次都返回一个 Pandas Series,触发大量 Python 对象创建和类型推断,底层无法利用 NumPy 的 C 级向量化能力。10 万行数据上,它可能比等效的向量化操作慢 50–100 倍。
常见错误现象:用 for idx, row in df.iterrows(): 做数值计算、条件赋值或字符串拼接,CPU 占用高但吞吐极低;尤其当循环体内调用 row['col'] 或 row[col_name] 时,开销进一步放大。
- 避免在
iterrows()循环里做任何可向量化的运算(如row['A'] + row['B']) - 不要用它来构造新列——直接用
df['new_col'] = df['A'] + df['B'] - 如果必须逐行逻辑(比如依赖前一行结果),优先考虑
shift()、cumsum()或apply()配合axis=1(但注意后者仍非真正向量化)
哪些操作能直接替换 iterrows() 而不写循环
绝大多数标量运算、布尔筛选、字符串方法都有对应向量化接口,关键是识别原始逻辑是否“行独立”。
例如:你想给每行生成 status 列,规则是 “若 score > 80 且 active == True,则为 'pass',否则 'fail'”——这完全可向量化:
立即学习“Python免费学习笔记(深入)”;
df['status'] = np.where((df['score'] > 80) & (df['active']), 'pass', 'fail')
- 数值计算:
df['C'] = df['A'] * 2 + df['B'].abs() - 条件赋值:
df.loc[df['age'] < 18, 'group'] = 'minor' - 字符串处理:
df['email_domain'] = df['email'].str.split('@').str[-1](注意.str方法全为向量化) - 时间处理:
df['month'] = df['date'].dt.month
apply() 什么时候能救场,什么时候反而更慢
apply() 本身不是向量化操作,只是语法糖。它的性能取决于传入函数能否被底层优化(如内置的 sum、mean)或是否触发 Series 构造。
错误用法:df.apply(lambda row: row['A'] + row['B'], axis=1) —— 这比 iterrows() 还慢,因额外封装开销。
- 优先用内置方法:
df[['A','B']].sum(axis=1)比apply(sum, axis=1)快得多 - 若必须自定义逻辑,先尝试
numpy.where、np.select或pd.cut - 真没法避免时,确保函数接收
Series或ndarray,而非依赖row['col']访问(改用位置索引row.iloc[0]稍快,但仍不推荐)
遇到“必须逐行依赖前序状态”的场景怎么办
比如计算余额:当前余额 = 上期余额 + 当期收入 - 当期支出。这类累积逻辑不能靠纯向量化一步到位,但也不该回到 iterrows()。
正确做法是识别模式并匹配 Pandas/Numpy 原生累积函数:
df['balance'] = df['income'].cumsum() - df['expense'].cumsum() + init_balance
- 累计类:用
cumsum()、cummax()、cumprod() - 差分类:用
diff()替代row[i] - row[i-1] - 滚动窗口:用
rolling().mean()等,而非手写滑动平均循环 - 真需要复杂状态机?考虑
numba.jit编译 Python 函数,或转成numpy.ndarray后用原生 NumPy 循环(此时已避开 Pandas 开销)
最易被忽略的一点:Pandas 的“向量化”本质是把操作压到底层 NumPy 数组上执行。只要你的逻辑不强制要求“按行顺序串行执行”,就几乎总能找到比 iterrows() 更快的路径——关键在拆解原始需求,而不是找“怎么让 iterrows() 快一点”。


















