异常值不只是噪音:从走势里读出信号
做数据整理的人都有个习惯动作:先画折线图,再看有没有刺眼的尖峰或深坑。2023年某电商平台的日订单量长期在8万到12万之间摆动,突然有一天跳到3.7万,第二天又回到9.1万。这个点如果直接删掉,确实能让图表更平滑,但当天发生了什么,可能就此被埋掉。异常值识别,本质上是判断一个点该被当作错误剔除,还是该被当作信息保留。这一步的选择,直接影响后续走势判断。
最朴素的方法是设定阈值。比如把均值加减三倍标准差之外的点标为异常。拿上面那组订单数据算,均值约10万,标准差约1.2万,3.7万就落在下界之外,会被自动标记。这个方法快,但有个前提:数据近似正态分布。现实中的访问量、交易额、搜索指数往往长尾明显,用标准差去卡,要么漏掉真正的异常,要么把正常的促销峰值误伤。2022年某内容平台的双十一流量冲到日常的4.8倍,按标准差法它全是异常点,可那恰恰是全年最有价值的观察样本。
换个思路,从走势本身出发。时间序列里,一个点是否异常,不取决于它离均值多远,而取决于它离“本该走到哪里”有多远。常见做法是用移动平均或指数平滑先拟合一条基准线,再看实际值与基准线的偏离幅度。比如某城市地铁日客流量,工作日稳定在420万人次上下,周末约300万。如果某个周三突然只有310万,偏离基准线26%,这个异常就值得追。它可能对应一场暴雨、一次线路故障,也可能对应数据采集端掉了某个站点的上报。偏离幅度加上发生时间,两个维度一起看,比单纯比大小有效得多。
季节性也是绕不开的一层。很多指标有周内规律和年度规律。某生鲜平台的蔬菜销量,每周一低迷、周五冲高,春节前一周能到平日的2.3倍。如果不先把季节因素剥掉,春节前的正常上涨会被误判为异常,而节后正常的回落又会被当成暴跌。操作上可以计算“同星期几、同月份”的历史中位数,用当前值除以这个中位数,得到一个季节调整后的比值。比值在0.85到1.15之间算平稳,超出这个区间再进入异常审查。这样做的好处是,把可解释的波动提前消化掉,剩下的异常才更接近真正需要关注的部分。
单一数据源的异常,往往说不清是业务变化还是采集故障。跨源对比是更稳的一步。比如某App的日活数据某天骤降18%,同时段的后端接口调用量却只降了3%,两者背离,大概率是埋点或统计口径出了问题,而不是用户真的走了。反过来,如果日活、接口调用、推送点击三个来源同步下滑,那更可能是外部事件,比如竞品发了大版本、或者运营商网络出了区域性故障。多源交叉不能直接给出答案,但能快速缩小排查范围,避免在错误的方向上做深度分析。
识别出来之后,处理方式同样需要克制。常见的做法有三种:直接删除、用插值填补、保留并标注。删除适合明确的采集错误,比如传感器瞬间断连产生的零值;插值适合缺失点,比如某天数据没上报,用前后几天的均值补上;而保留并标注,适合那些原因不明但真实发生的异常。后一种在趋势观察里尤其重要,因为很多趋势的转折,最早就是以一个不起眼的异常点出现的。2019年某出行平台的夜间订单连续三周在凌晨两点出现小幅跳升,当时被当作噪声,三个月后回看,那是夜班经济起量的起点。
工具层面,现在有不少现成方法可以用。基于四分位距的箱线图规则,简单且对偏态数据更稳健;STL分解能把趋势、季节和残差拆开,残差项里的极端值就是候选异常;还有基于密度的算法,适合多维数据里找离群点。但工具不解决判断问题。一个异常值要不要跟进,取决于它出现的频率、持续的时间、以及是否伴随其他指标的同步变化。单次、短暂、无伴随的异常,多数可以先记录在案;连续出现、持续超过三个周期、并且有跨源印证的异常,才值得投入精力去深挖。
回到开头那个3.7万的订单量。后来查下来,是当天凌晨支付网关做了两小时升级,部分订单被延迟到次日统计。这个异常点本身没有业务含义,但它暴露了统计口径和系统运维之间的时间差。如果当时直接删掉,这个口径问题可能还会在下一次升级时重演。异常值识别的价值,不在于把图做干净,而在于让每一个偏离走势的点,都有机会被解释清楚。解释不了的,先留着,标注好,等更多的数据来回答。