for循环处理DataFrame极慢是因为Python层逐行解释执行,需反复索引查找、类型检查和对象封装;应优先使用向量化操作、合理dtype及.iloc等高效索引方式。

为什么 for 循环处理 DataFrame 就慢得离谱
因为 pandas 的 for 循环本质是 Python 层逐行解释执行,每次都要触发索引查找、类型检查、对象封装,而底层数据其实早就在内存里排好了——你却绕开它,自己手写“走路式”遍历。
常见错误现象:df['new_col'] = [func(x) for x in df['col']] 或更糟的 for i in range(len(df)):;看起来简洁,实际比向量化慢 10–100 倍,尤其在 >10k 行时延迟明显。
- 优先用
.apply()配合axis=1仅当逻辑无法拆解到列粒度时才考虑(且尽量传vectorize=True) - 绝大多数标量运算应直接写成向量表达式:
df['a'] + df['b'] * 2,不是df.apply(lambda r: r['a'] + r['b'] * 2, axis=1) - 字符串/时间操作别用
.str或.dt以外的方式:比如提取年份,用df['date'].dt.year,别写df['date'].apply(lambda x: x.year)
np.vectorize 不是加速器,而是“语法糖包装器”
它看起来像向量化,但底层仍是 Python 循环调用你的函数,没有跳过解释器开销。唯一价值是让自定义函数能接受数组输入并返回数组,方便接口对齐。
使用场景:仅当你有一段**必须用 Python 写、无法改造成 NumPy 原生操作**的逻辑(比如查字典、调外部 API),又想让它“长得像向量化”以便嵌入 pipeline。
立即学习“Python免费学习笔记(深入)”;
- 真正提速要靠
np.where、np.select、布尔索引替代 if-else 链 - 复杂条件判断优先用
pd.cut()或pd.qcut(),而不是np.vectorize+ 手写分段函数 - 如果函数里有
try/except或 I/O,np.vectorize不会变快,只会掩盖性能问题
pandas .loc 和 .iloc 的写法差异直接影响速度
看似只是取数方式不同,但 .loc 走标签对齐+索引查找路径,.iloc 直接按内存偏移读取。对已排序且无重复索引的 DataFrame,.iloc 稳定快 2–5 倍;若索引混乱或含字符串,.loc 可能触发哈希构建,首次访问延迟明显。
- 批量赋值优先用
.iloc[:, [0, 2, 5]] = ...,别用.loc[:, ['col_a', 'col_c', 'col_f']] = ...(除非你真依赖列名语义) - 条件筛选后立刻要取值?用
df.iloc[mask_array]比df.loc[mask_array]少一次索引映射 -
.at和.iat适合单点读写:前者按标签,后者按位置,都比.loc/.iloc快一个数量级
用 dtype 控制内存占用比“换机器”更有效
默认 int64 或 float64 在小范围整数或精度要求不高的场景下纯属浪费。pandas 读取 CSV 时若不指定类型,会把本可存为 int8 的 flag 列存成 int64,内存涨 8 倍,缓存命中率暴跌,连带计算也变慢。
- 读 CSV 时加
dtype={'flag': 'uint8', 'category_col': 'category'} - 数值列用
pd.to_numeric(..., downcast='integer')自动降级 - 字符串列超过 50% 值重复?立刻转
astype('category'),内存常减 70%,.groupby也更快
向量化不是魔法,是让 CPU 连续读内存块、避免跳转和对象分配。所有优化动作,最终都在跟内存布局和解释器开销较劲。最常被忽略的,其实是读数据那一刻的 dtype 选择——后面怎么向量化,都补不回一开始多占的那几 GB 内存。


















