异常值识别:别急着删,先看清它从哪来
做数据的人迟早会撞上一个尴尬时刻:某天销售额突然翻了三倍,或者某台服务器的响应时间从80毫秒跳到4秒。第一反应往往是“脏数据”,但删之前最好停三秒。异常值识别真正的难点从来不是算出一个分数,而是判断这个极端值到底代表错误、代表罕见事件,还是代表系统发生了变化。三种可能对应三种完全不同的处理方式,搞混了,后面的走势分析就全歪了。
先看一个具体场景。某电商后台的客单价分布里,绝大多数订单落在60到300元之间,但有一笔订单金额是47000元。按3σ原则,均值的三个标准差之外都算异常,这笔订单会被直接标记。可如果去查,发现是一个企业客户一次性采购了200件商品,那就是真实交易,删掉等于把当月GMV削掉一块。反过来,如果这笔金额来自测试账号,那它确实是噪声。同一个数字,两种身份,处理方法相反。所以第一步不是跑算法,而是回到业务口径:这个字段的正常范围由谁定义,录入环节有没有可能产生极端值。
统计方法里最常用的是基于距离的判断。Z-score假设数据近似正态分布,把每个点减去均值再除以标准差,绝对值超过3就报警。它对单点异常敏感,但有个明显短板:均值和标准差本身会被极端值拉偏。一组原本均值100的数据里塞进一个10000,标准差会被撑大,反而让其他异常点显得“正常”。更稳的做法是改用中位数和四分位距。IQR方法把上下界设在Q1减1.5倍IQR和Q3加1.5倍IQR,对偏态分布更宽容。同样那笔47000元的订单,在IQR框架下依然会被标出,但不会因为它的存在而改变边界,这一点在做长期数据观察时很重要。
时间序列场景则不能只看单点偏离。某API的延迟平时稳定在80毫秒上下,某天下午突然连续五分钟跑到900毫秒。如果孤立地看,每个900都是异常值;但连起来看,这是一段持续事件,很可能对应一次发布、一次网络抖动或一次流量尖峰。这时候用移动平均加残差判断更合适:先算出过去一小时的滚动均值,再看当前值与均值的偏离是否超过阈值。孤立点可以容忍,连续偏离才值得追。很多监控系统误报率高,就是因为把单点抖动和持续劣化混为一谈。
还有一个容易被忽略的角度是分组。整体数据里正常的值,放进某个子群里可能就成了异常。比如全国门店的日销售额中位数是8000元,一家门店做到15000元不算离谱。但如果按城市层级拆开,三线城市门店的分布中位数只有4000元,那15000元就值得多看一眼——它可能是当地一次团购活动,也可能是数据重复上报。分组之后再识别,能过滤掉大量因为业务结构差异造成的假阳性。代价是每组样本变少,阈值需要相应放宽,否则小样本下的波动会被误判。
识别出来之后怎么处理,往往比怎么识别更考验判断。常见做法有三种:删除、替换、保留并标记。删除适合确认的录入错误;替换适合用中位数或均值补全缺失;保留并标记适合那些可能是真实信号的极端值。第三种最容易被低估。金融风控里,一笔远超历史区间的交易不会直接被删,而是进入人工复核队列——它可能是欺诈,也可能是大客户。把异常值当成待验证的线索而不是待清理的垃圾,这个视角转换能让数据质量工作少走很多弯路。
回到开头的那个问题:异常值该不该删。没有统一答案,但有一个可操作的顺序——先查来源,再看分布,最后结合业务判断。来源能解释的,按业务规则处理;来源不明但统计上极端孤立的,标记待查;来源不明且成簇出现的,优先当作系统变化的信号。数据观察做久了会发现,真正有价值的信息常常藏在那些“不太对劲”的点里,而识别方法只是帮你把它们挑出来,判断还得靠人。