apply在axis=1下特别慢,因其本质是Python层逐行循环,每行转Series并调用函数,引发对象创建、属性查找、类型检查等解释器开销,且无法利用NumPy底层C实现和CPU向量化指令。

为什么 apply 在 axis=1 下特别慢?
因为 apply 在 axis=1 时,本质是 Python 层面的逐行循环:每行被包装成一个 Series 对象,再调用你的函数一次。这触发了大量 Python 解释器开销——对象创建、属性查找、类型检查、函数调用栈压入/弹出。而向量化操作(如 df['A'] + df['B'])直接调用底层 NumPy 的 C 实现,跳过所有 Python 循环,对整列做一次内存连续运算。
axis=1 的 apply 还有哪些隐藏成本?
除了循环本身,这些细节也在拖慢速度:
-
raw=False(默认):每行传入的是带索引的Series,不是纯数组,额外有 label 查找和对齐开销 -
result_type=None:返回结果类型需动态推断,尤其当函数返回 list 或 dict 时,会触发额外结构解析 - 无法利用 CPU 向量化指令(如 AVX):Python 函数体内的计算仍是标量执行,哪怕你用了
np.log,也受限于单次调用上下文 - GC 压力大:百万行 = 百万个临时
Series对象,频繁触发垃圾回收
怎么验证你真在用 axis=1 的 apply?
别只看代码写了 axis=1,还要确认实际执行路径:
- 用
%%time或timeit测真实耗时,而非“看起来像向量化” - 检查函数体内是否出现
row['col_name']或row.index—— 这说明你依赖了Series接口,无法被raw=True加速 - 如果函数只读取固定几列数值,且逻辑可拆解,优先改写为布尔掩码 +
loc赋值,例如:df.loc[df['e'] == 10, 'new'] = df['c'] * df['d']
什么情况下还不得不写 axis=1 的 apply?
只有当逻辑真正无法用向量化表达式描述时才保留它,比如:
- 需要调用外部 API 或文件 I/O(但这时性能瓶颈已不在 Pandas)
- 函数内部有状态依赖(如累计计数、上一行值引用)——此时应考虑
shift+ 累积函数,或改用numba.jit - 涉及复杂字符串解析(正则捕获组嵌套、多步替换),且
.str方法链无法覆盖
即便如此,也建议先尝试 swifter.apply 并行化,或把逻辑抽到 numba.vectorize 中——但要注意:一旦你开始为 apply 找加速方案,就说明问题本身可能更适合换种建模方式。










