时间序列里那些容易踩的坑,我攒了五年
2019年我第一次独立处理一份销售流水,3300行记录,日期从2017年1月到2019年6月。导入之后直接画折线图,发现2018年2月有一个巨大的坑,销售额只有平时的四分之一。当时第一反应是业务出了问题,查了三天原始单据,最后发现那段时间春节假期加系统升级,有9天没有产生任何记录。缺失的9天被默认当成了零,于是折线图上就出现了一个假的低谷。从那以后,处理任何时间序列之前,我都会先做一件事:检查时间轴的连续性,用日期生成一张完整的日历表,左连接原始数据,看看有多少空行。这个动作只需要两分钟,但能避免后面80%的误判。
缺失值怎么补,取决于它产生的原因。如果是设备故障导致的连续缺失,比如传感器离线4个小时,线性插值通常可以接受;如果是业务本身没有发生,比如周末没有交易,那缺失就是真实的零,不应该补。我见过一份日活数据,运营同事直接把缺失日填充成前一天的值,结果那个月的走势看起来异常平滑,掩盖了一次持续6天的服务中断。更稳妥的做法是给每个缺失点打上标记,在图表上用虚线或者空心点表示,这样观察走势的时候心里有数。数据清洗不是为了让曲线好看,而是为了让判断更接近事实。
平稳性这个概念,教科书上讲得比较抽象。我的理解很简单:如果一段序列的均值和方差在不同时间段内大致稳定,那它就可以被当作平稳序列来处理。举个例子,某电商平台的日订单量,周一到周四平均在2.3万单左右,周五到周日平均在3.8万单左右,整体均值一直在变,这就是不平稳。遇到这种情况,通常做一阶差分,也就是用今天减去昨天,得到一个新的序列。差分之后如果均值不再有明显漂移,很多基础方法就能用了。但要注意,差分次数不是越多越好,差分会放大噪声,也会损失一个数据点。一般做一次就够,除非有明确的趋势需要消除。
走势分解是我平时用得最多的工具。把一条时间序列拆成趋势项、季节项和残差项,能帮我们看清楚波动到底来自哪里。比如一份连续18个月的客流量数据,原始曲线上下震荡,看不出方向。分解之后发现趋势项在缓慢下降,季节项显示每年7月和12月有两个高峰,残差项在某个特定月份突然变大,一查是附近修路影响了到店人数。如果不做分解,这三个信息混在一起,很容易得出“客流总体稳定”的结论。分解工具不需要多复杂,移动平均就能做趋势项,月度均值就能做季节项,剩下的就是残差。关键是养成拆开看的习惯。
自相关图是另一个容易被忽视的常识。它衡量的是序列和自身滞后版本之间的相关性。如果一个序列在滞后7天处有很高的自相关,说明存在以周为单位的周期。我处理过一份服务器请求量数据,自相关图在滞后1天处显著,在滞后7天处也显著,说明既有日周期又有周周期。这个信息直接决定了后续建模时要不要加入傅里叶项或者周期虚拟变量。很多人在这一步跳过,直接上模型,结果预测精度上不去,回头查原因,往往就是周期没提取干净。自相关图花五分钟就能画出来,比事后调参划算得多。
关于预测,有一个常识值得反复提醒:所有预测都建立在历史模式会延续的假设上。2020年之前,很多线下门店的客流预测模型表现很好,因为周末高于工作日的规律持续了几年。但外部条件突变时,这个假设就不成立了。我的做法是,任何预测结果都配一个置信区间,并且定期用滚动窗口回测。比如用前12个月预测第13个月,然后向前滚动,看预测误差是否稳定。如果误差突然变大,说明历史模式可能正在改变,这时候模型本身没问题,是数据生成过程变了。预测的价值不在于给出一个精确数字,而在于提供一个基准,让我们知道实际走势偏离了多少。
最后说一个操作层面的习惯:画图之前先看统计量。均值、中位数、最大值、最小值、标准差,这五个数字花30秒扫一眼,能发现很多异常。比如某次我拿到一份月度数据,均值是1200,中位数是980,标准差是450,最大值是3200。均值远高于中位数,说明分布右偏,大概率存在少数极端高值。顺着这个线索查下去,发现有两个月的数值是其他月份的3倍,原因是那两个月包含了季度结算的集中录入。如果直接把这些数据丢进模型,预测结果会被拉高。先看统计量,再决定要不要做缩尾或者分段处理,这个顺序比反过来更省时间。