Pandas异常值处理:置为NaN与直接删除的差异原因解析
Pandas分位数筛选两种写法的差异原因
两种写法结果不同的核心原因是:传入df[]的筛选条件结构完全不同,触发了pandas完全不同的索引逻辑。
第一种写法将异常值置为NaN的原理
你第一种写法用的筛选条件是(df < ub) & (df > lb):
- 首先
lb = df.quantile(0.01)、ub = df.quantile(0.99)的返回值是Series类型,索引为原DataFrame的列名(你的测试数据里只有calories一列,所以这个Series长度为1,索引是['calories'])。 - 当整个DataFrame和这个Series做大小比较时,pandas会按列名对齐做广播运算,最终返回一个和原DataFrame形状完全一致的布尔DataFrame,每个单元格的值对应当前位置的数值是否在分位数范围内。
- Pandas的索引规则里,如果传入
df[]的是同形状的布尔DataFrame,不会做行删除,而是执行逐单元格的掩码逻辑:值为True的位置保留原数值,值为False的位置填充NaN。这就是你看到异常值被替换为空值的原因,本质是单元格级别的值替换,不是行筛选。
你可以自行打印验证条件的结构:
cond1 = (df < ub) & (df > lb) print(type(cond1)) # <class 'pandas.core.frame.DataFrame'> print(cond1.shape) # (19, 1),和原df形状完全一致
第二种写法直接删除异常行的原理
你第二种写法用的筛选条件是(df["calories"] < q_hi) & (df["calories"] > q_low):
- 这里
q_low = df["calories"].quantile(0.01)、q_hi = df["calories"].quantile(0.99)的返回值是标量数值,不是Series。 - 整个比较逻辑是在单列Series上执行的,最终返回一个长度和原DataFrame行数一致的一维布尔Series,每个位置对应当前行的
calories值是否在分位数范围内。 - Pandas的索引规则里,如果传入
df[]的是长度和行数匹配的一维布尔序列,会执行行级筛选:值为True对应的整行保留,值为False对应的行直接丢弃。这就是异常行被直接删除的原因。
同样可以打印验证条件结构:
cond2 = (df["calories"] < q_hi) & (df["calories"] > q_low) print(type(cond2)) # <class 'pandas.core.series.Series'> print(len(cond2)) # 19,和原df行数一致
两种操作的功能差异总结
- 第一种是单元格级掩码操作:会保留原数据的所有行,仅把不符合条件的单元格替换为
NaN,如果是多列DataFrame,会自动按每列各自的分位数判断,逐列替换不符合条件的值。 - 第二种是行级筛选操作:不会保留不符合条件的行,只要指定列的数值不满足阈值,整行都会被剔除,适合直接过滤异常样本的场景。
内容的提问来源于stack exchange,提问作者Shooting Stars
相关产品推荐
相关产品推荐

