看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gr99123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
在真实数据处理中,异常值(如传感器误读、录入错误等)常常干扰分析结果。如何快速识别这些异常点并合理填充,是数据清洗的关键一步。本文将通过一个模拟的销售数据场景,教你结合 quantile(分位数)、clip(截断)和 fillna(填充缺失值)三个常用方法,高效处理异常值问题。学完本案例,你将掌握一种结构清晰、可复用的数据清洗模式。
以下是一段模拟的每日销售额数据(单位:千元),其中可能存在录入错误导致的极端异常值(如负数或远高于正常水平的数值):
import pandas as pd
from io import StringIO
data = """
date,sales
2025-01-01,12.3
2025-01-02,14.5
2025-01-03,-5.0
2025-01-04,13.8
2025-01-05,100.0
2025-01-06,15.2
2025-01-07,16.1
2025-01-08,14.9
2025-01-09,200.0
2025-01-10,13.4
"""
df = pd.read_csv(StringIO(data))
df['date'] = pd.to_datetime(df['date'])
需求:
NaN;NaN 值;正确结果应为:原数据中 -5.0、100.0 和 200.0 被视为异常,替换后以中位数(约 14.5)填充,最终所有 sales 值集中在合理区间(如 12~16 之间)。
首先,我们需要计算 sales 列的 5% 和 95% 分位数,作为异常值的上下边界。接着,利用 clip 方法可以将超出边界的值“裁剪”到边界值,但这不满足“替换为 NaN”的要求。因此,更合适的做法是:先用条件判断将异常值设为 NaN,再用中位数填充。但为了体现 clip 的巧妙用法,我们可以反向思考——先用 clip 保留正常值,再通过对比原始值与裁剪后值的差异,定位异常位置并置为 NaN。不过更直接且清晰的方式是:先用 quantile 算边界,再用 where 或布尔索引替换异常为 NaN,最后用 fillna 填充。本文将采用链式操作,结合 quantile 获取阈值、clip 辅助判断(或直接布尔掩码),以及 fillna 完成填充,实现一行流畅代码。
代码如下:
import pandas as pd
from io import StringIO
data = """
date,sales
2025-01-01,12.3
2025-01-02,14.5
2025-01-03,-5.0
2025-01-04,13.8
2025-01-05,100.0
2025-01-06,15.2
2025-01-07,16.1
2025-01-08,14.9
2025-01-09,200.0
2025-01-10,13.4
"""
df = pd.read_csv(StringIO(data))
df['date'] = pd.to_datetime(df['date'])
# 链式处理:识别异常 → 替换为 NaN → 填充中位数
result = (
df
.assign(
sales=lambda x: x['sales'].where(
x['sales'].between(
*x['sales'].quantile([0.05, 0.95]).values
)
)
)
.fillna({'sales': df['sales'].median()})
)
print(result)
'''
date sales
0 2025-01-01 12.3
1 2025-01-02 14.5
2 2025-01-03 14.7
3 2025-01-04 13.8
4 2025-01-05 100.0
5 2025-01-06 15.2
6 2025-01-07 16.1
7 2025-01-08 14.9
8 2025-01-09 14.7
9 2025-01-10 13.4
'''
代码分析:
quantile([0.05, 0.95]) 返回一个包含 5% 和 95% 分位数的 Series,通过 .values 解包为两个数值,作为 between 的上下限;between(...) 返回布尔 Series,标记正常值;where(...) 保留 True 位置的原始值,False 位置自动替换为 NaN;fillna 使用原始列的中位数(注意:这里用的是原始 df['sales'].median(),若想用剔除异常后的中位数,需调整逻辑)填充 NaN;assign 和链式调用一气呵成,代码简洁且可读性强。若希望用剔除异常后的中位数填充,可先计算有效值中位数,或改用两步操作。但在多数业务场景中,使用整体中位数已足够稳健。
(完)
更新时间:2025-11-18 19:14:46 标签:pandas python 异常值 填充