Pandas的条件赋值几乎是数据清洗绕不开的操作。无论是把异常值替换成缺失值,还是根据分数划分等级,本质都是同一个问题:先按条件筛选出行,再对这些行的某些列重新赋值。看似简单,但不同写法在性能、可读性和适用范围上差别不小,用错了还可能触发著名的SettingWithCopyWarning警告。本文围绕几种主流方案逐一展开,配合可运行的代码示例,帮你彻底搞清楚每种方法的适用边界。

一、loc条件索引:最推荐的官方写法
loc是Pandas官方文档明确推荐的条件赋值方式。它的语法结构是df.loc[条件, 列名] = 新值,逗号前是行筛选条件,逗号后指定要修改的列,一次调用就完成筛选和赋值,语义非常清晰。先构造一份示例数据:
import pandas as pd
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'销量': [80, 150, 200, 60],
'城市': ['北京', '上海', '北京', '广州']
})
# 把销量大于100的行,销量列改为0
df.loc[df['销量'] > 100, '销量'] = 0
print(df)执行后销量为150和200的两个值会变成0。这种写法最大的好处是它是原地修改,直接作用在DataFrame本体上,不会产生副本,因此不会触发链式索引警告。条件部分支持任意布尔表达式,比如(df['销量'] > 100) & (df['城市'] == '北京'),注意多条件组合时每个条件要用圆括号包起来,且必须用&和|,不能用Python原生的and、or。
如果要同时修改多列,列名部分传入列表即可:df.loc[df['销量'] > 100, ['销量', '城市']] = [0, '未知']。这种一行代码完成多列联动更新的能力,是很多其他方法不具备的。另外,loc还支持用另一个Series或DataFrame来赋值,例如按行匹配另一个映射表更新,灵活性相当强。
二、np.where:适合二分场景的向量化方案
如果需求是满足条件取A值、不满足取B值这种二选一的场景,numpy.where往往比loc更顺手。它返回一个全新的数组,条件成立的位置取第二个参数,否则取第三个参数:
import numpy as np df['等级'] = np.where(df['销量'] > 100, '优秀', '一般') print(df)
这一行代码直接生成了新的等级列,销量大于100的标记为优秀,其余为一般。np.where是向量化操作,底层由NumPy的C实现驱动,在几十万行的数据上执行速度远快于Python层面的循环,这是它相对apply的显著优势。
需要注意的一点是np.where是整体替换而非条件修改,也就是说不满足条件的位置也会被重新赋值。如果原始列里已有数据且只想改部分值,直接用np.where覆盖会把不该改的也改掉。解决办法是把第三个参数设为原列本身:df['销量'] = np.where(df['销量'] > 200, 200, df['销量']),这样就实现了只封顶不动的效果,常用于异常值截断。
三、apply与map:灵活但性能一般的选择
当条件逻辑复杂到需要写多行判断时,apply配合自定义函数是最直观的方案。比如根据销量划分多个等级:
def get_level(x):
if x >= 200:
return 'A'
elif x >= 100:
return 'B'
else:
return 'C'
df['等级'] = df['销量'].apply(get_level)apply会对列中每个元素执行函数,逻辑想写多复杂都行,可读性也不错。如果函数足够短,也可以用lambda表达式一行搞定:df['等级'] = df['销量'].apply(lambda x: 'A' if x >= 200 else 'B')。但它的缺点同样明显:逐元素调用Python函数,性能比向量化操作低一个量级。数据量在百万行级别时,apply可能需要几秒,而np.where几乎是瞬间完成。
对于多分段的场景,其实还有更好的向量化替代品:pd.cut。例如pd.cut(df['销量'], bins=[0, 100, 200, 1000], labels=['C', 'B', 'A']),一行代码完成区间分段,性能和简洁度都优于apply。而map则适合精确值映射,比如把城市名换成编码:df['城市编码'] = df['城市'].map({'北京': 1, '上海': 2, '广州': 3}),字典里没有匹配到的值会变成NaN,这一点要留意。
四、replace与mask:容易被忽视的补充工具
replace适合精确值替换,比如df['城市'].replace('北京', 'Beijing')。它还支持传入字典一次替换多个值,并且可以设置正则匹配:df.replace({r'^北.*': '北方城市'}, regex=True)。不过replace基于值匹配而非布尔条件,做不了大于、小于这类比较判断,所以它是条件赋值的补充而非替代。
mask和where是另外一对好用的工具。df['销量'].mask(df['销量'] > 150, 999)表示条件成立的位置替换成999,不成立的位置保留原值;而where正好相反,条件成立保留、不成立才替换。这两个方法语义明确,特别适合只改一半数据的场景,配合inplace或重新赋值即可生效。
五、方法选择建议与常见坑
总结一下选择思路:简单的条件赋值优先用loc,这是最不容易出错的方式;二值分支用np.where;多区间分段优先pd.cut;复杂逻辑或需要访问多列时用apply;精确值替换用map或replace。数据量大时尽量避开apply,向量化方法能带来数量级的性能提升。
最后提醒一个新手高频踩的坑:千万不要写df[df['销量'] > 100]['销量'] = 0这种链式索引。这种写法先取子集再赋值,操作的是临时副本,原DataFrame根本不会被修改,而且Pandas会抛出SettingWithCopyWarning警告。看到这个警告就该意识到写法有问题,改成df.loc[df['销量'] > 100, '销量'] = 0即可。另外,条件赋值前建议先用df['销量'].dtype确认列的数据类型,把字符串和数字混用会导致类型推断异常,这些细节处理好了,条件赋值就不会再出幺蛾子。
Pandas条件赋值DataFrame修改列值loc函数用法修改时间:2026-09-13 23:10:57