apply在条件筛选中又慢又难调试,因其本质是隐式循环且强制转object dtype导致布尔索引失效;90%场景可用向量化布尔表达式替代,速度提升5–50倍,逻辑更清晰。

为什么 apply 在条件筛选时又慢又难调试?
因为 apply 默认按行(axis=0)或按列调用 Python 函数,本质是隐式循环,无法被 Pandas 底层的 NumPy 向量化加速;更关键的是,它会强制把整列转成 object dtype(尤其混入 None 或字符串时),后续布尔索引可能失效或报 TypeError: boolean index did not match indexed array。
真实场景中,90% 的「根据多列值做 if-else 判断再筛选」都能用向量化布尔表达式替代,速度通常快 5–50 倍,且逻辑一目了然。
用 loc + 布尔表达式直接筛选(最常用)
这是绝大多数条件筛选的首选方式。核心是把判断逻辑拆成原子布尔子表达式,用 &(与)、|(或)、~(非)连接,注意必须用括号包裹每个子表达式。
-
df.loc[(df['age'] > 25) & (df['city'].str.startswith('Sh')) & (df['score'].notna())]—— 多条件组合,str.startswith和notna()都是向量化方法 - 避免写成
df['age'] > 25 & df['city'].str.startswith('Sh'):运算符优先级导致结果错乱 - 字符串比较慎用
== 'xxx',遇到NaN会得False;改用eq('xxx')或配合fillna(False) - 数值范围判断别用
apply(lambda x: 10 ,直接写 <code>(df['x'] > 10) & (df['x']
用 numpy.select 或 numpy.where 构造新列再筛选
当条件逻辑复杂(如嵌套 if-elif-else)、且需复用结果时,比链式 loc 更清晰。它们返回的是纯 NumPy 数组,类型稳定,不会因 apply 引入 dtype 混乱。
立即学习“Python免费学习笔记(深入)”;
-
cond_list = [df['score'] >= 90, df['score'] >= 80, df['score'] >= 70],choice_list = ['A', 'B', 'C'],然后df['grade'] = np.select(cond_list, choice_list, default='F') - 二选一场景优先用
np.where((df['a'] > 0) & (df['b'] == 'x'), 'yes', 'no'),比apply快且支持广播 - 注意
np.select中cond_list的顺序是自上而下匹配,第一个为True的条件生效,类似 if-elif - 如果条件涉及
NaN,用pd.isna()显式判断,不要依赖== np.nan
哪些情况真没法向量化,必须用 apply?
不是所有逻辑都能避开 apply,但要先确认是否真的不可替代:
- 调用外部 API、文件读写、正则捕获组提取(
.str.extract()能覆盖 80% 场景)、或依赖全局状态的函数 - 逻辑中需要动态 import、执行
eval、或使用未向量化的第三方库函数(如某些 NLP 工具) - 即使必须用
apply,也应指定axis=1并用raw=True(传入 ndarray 而非 Series),减少对象开销 - 最后检查输出 dtype:若结果全是字符串或数字,手动转
astype(str)或astype(float),避免后续布尔操作失败
向量化筛选的关键不在“会不会写”,而在“敢不敢把 if 分解成布尔表达式”。一旦习惯用 & 替代 and、用 isna() 替代 == None,多数 apply 就自然消失了。真正容易被忽略的是空值传播行为——同一个条件,在 loc 中返回 False,在 np.where 中可能返回 NaN,这直接影响筛选结果的完整性。


















