ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

时间序列预测中异常值处理的6大策略与实战指南

时间序列预测中异常值处理的6大策略与实战指南 1. 从一次失败的预测说起为什么异常值处理是基本功去年我接手了一个零售门店的销量预测项目数据看起来挺规整但模型跑出来的结果总是“抽风”——在某些节假日预测值会莫名其妙地比实际值低一大截或者反过来在看似平常的工作日给出一个高得离谱的预测。排查了很久最后发现问题出在数据本身历史销量数据里混杂着几次大型促销活动、门店盘点导致的闭店甚至还有一次因为系统故障产生的数据异常。这些点在时间序列里就是典型的“异常值”。如果不处理它们模型就会把这些“噪音”当成“信号”去学习导致预测精度一塌糊涂。这让我深刻意识到在时间序列预测这个领域数据清洗尤其是异常值处理绝不是可有可无的预处理步骤而是决定模型预测能力上限的基石。很多人一提到时间序列预测脑子里立刻蹦出的是ARIMA、LSTM、Transformer这些高大上的模型恨不得马上调包跑起来。但我的经验是在考虑用什么模型之前至少要把70%的精力花在理解数据和清洗数据上。异常值处理就是清洗环节中最关键、也最容易被忽视的一环。它处理的不是“脏数据”而是那些真实发生、但不符合常规模式的“特殊事件”。处理得好模型能更清晰地捕捉到真正的趋势和规律处理不好再先进的模型也只会“垃圾进垃圾出”。今天我们就来系统性地拆解时间序列中的异常值处理。我不会只给你罗列方法而是会结合我踩过的坑告诉你每种方法背后的逻辑、适用场景以及最容易被忽略的实操细节。我们的目标很明确从数据根源上提高预测精度。无论你用的是传统的统计方法还是LSTM、Transformer这类深度学习模型这套处理思路都是通用的。2. 识别异常值先搞清楚你要对付的是什么在动手处理之前我们必须先明确目标时间序列里的异常值到底是什么根据我的经验可以粗略分为三大类每一类的处理策略都截然不同。第一类点异常。这是最常见、也最直观的异常。指的是在某个特定时间点上观测值明显偏离序列的整体模式。比如电力消耗数据中某个时刻的尖峰可能是设备故障或测量错误电商销量数据中某天的暴增可能是一次成功的营销活动。点异常的特点是“孤立”它只影响一个或几个连续的点。第二类上下文异常。这类异常更具欺骗性。单个数据点看可能完全正常但放在特定的上下文如季节性模式里就显露出异常。例如在具有明显周周期性的销售额数据中周一的销售额通常较低。如果某个周一的销售额看起来“正常”但相比历史同期所有周一的销售额却异常高那么它就是一个上下文异常。这类异常用简单的阈值法很难发现。第三类集体异常。指的是一连串数据点作为一个整体表现出异常模式但其中的单个点可能并不异常。比如一段持续数小时的平稳传感器读数本应是波动的或者一段持续数天的销量低迷期本应是平稳的。这种异常往往预示着系统状态的改变如设备进入待机、市场进入淡季。那么如何发现它们呢单纯靠肉眼观察是不现实的尤其是面对海量数据时。我们需要借助一些工具和方法进行初步筛查可视化是第一步也是最重要的一步。绘制时序图是最基本的方法。但我强烈建议同时绘制滚动统计量图比如滚动均值±3倍滚动标准差的范围。这能动态地展示序列的局部波动情况比全局阈值更敏感。# 示例使用pandas计算滚动统计量 import pandas as pd import matplotlib.pyplot as plt # 假设df[value]是你的时间序列 rolling_mean df[value].rolling(window30, centerTrue).mean() # 30期滚动均值 rolling_std df[value].rolling(window30, centerTrue).std() df[upper] rolling_mean (3 * rolling_std) df[lower] rolling_mean - (3 * rolling_std) plt.figure(figsize(12,6)) plt.plot(df.index, df[value], label原始序列) plt.plot(df.index, rolling_mean, label滚动均值, colororange) plt.fill_between(df.index, df[lower], df[upper], colorgray, alpha0.2, label±3σ范围) plt.legend() plt.show()在这张图上落在灰色区域之外的点就可能是潜在的异常点。箱线图与分位数统计。对于不具强趋势和季节性的序列箱线图基于IQR即四分位距是一个快速识别离群点的好工具。但在时间序列中直接使用全局箱线图会忽略时间依赖性所以更佳实践是对序列进行分解后对残差部分应用箱线图。利用STL分解进行异常初筛。STLSeasonal-Trend decomposition using Loess是一种鲁棒性强的时间序列分解方法能有效处理异常值。它将序列分解为趋势项、季节项和残差项。残差项中蕴含了原始序列中无法被趋势和季节解释的部分异常值往往会在这里被放大显现。from statsmodels.tsa.seasonal import STL # 进行STL分解 robust参数设为True可以提高分解对异常值的鲁棒性 stl STL(df[value], period12, robustTrue) # period根据你的数据周期设定 result stl.fit() resid result.resid # 然后对resid使用3-sigma原则或IQR方法找出异常索引通过分析STL分解后的残差我们可以更精准地定位那些在“剔除了常规模式后”依然突出的点。记住识别阶段的目标是“发现嫌疑对象”而不是“最终定罪”。我们需要结合业务知识来判断这个点真的是错误吗还是一次有意义的特殊事件这是区分“噪声”和“信号”的关键也直接决定了我们后续是“修正”它还是“保留”它。3. 处理策略一直接修正与平滑处理当我们确认某些点是真正的“噪声”或“错误数据”时如传感器瞬时报错、数据传输丢失最直接的想法就是修正它。这里主要有两种思路用合理的值替换或者对其进行平滑以削弱影响。3.1 直接替换法简单粗暴有时最有效直接替换的核心在于用什么值来替代异常值。选择不同背后的假设也不同。用缺失值NaN替换这是最保守的做法。相当于承认“我不知道这里应该是什么”把问题留给后续的缺失值填充步骤去处理。这适用于异常点毫无规律、且周围数据也无法提供可靠信息的情况。在Python中pandas可以轻松实现df.loc[anomaly_index, value] np.nan。用前后邻居的统计量替换用前一个值或后一个值填充df[value].fillna(methodffill)或bfill。这假设序列在短时间内的变化是平滑的。适用于高频数据如秒级、分钟级中的瞬时毛刺。用滚动均值/中位数替换例如用异常点前后5个窗口的均值来替换它。这比用单个邻居更稳定。replacement_value df[value].rolling(window11, centerTrue, min_periods1).mean().iloc[anomaly_index]。这里window11表示以异常点为中心前后各5个点。用线性插值替换df[value].interpolate(methodlinear)。这假设两个正常点之间的变化是线性的比用单一值填充更合理。注意直接使用前后值或插值法在异常点成片出现时会引入问题。如果连续多个点都是异常用它们互相填充会传播错误。此时用更远窗口的统计量如日同期均值、周同期均值会更安全。3.2 平滑法让刺耳的“噪音”变得柔和如果异常值并非完全错误只是过于“尖锐”我们可能不想完全移除它而是想平滑它的影响。这就是平滑法的用武之地。移动平均最简单的方法。用一个窗口内的平均值来代替中心点的值。但它有个明显缺点会产生“滞后”。因为平均值包含了未来的信息在中心窗口时或者你只能用历史窗口导致平滑后的序列相位落后。# 简单移动平均窗口为5使用过去4个点和当前点 df[smoothed] df[value].rolling(window5, min_periods1).mean()指数平滑给近期数据更高的权重远期数据更低的权重。这种方法对最近的突变更敏感滞后效应比简单移动平均小。pandas的ewm函数可以方便实现。# 指数加权移动平均alpha为平滑因子0alpha1越小越平滑 df[ewm_smoothed] df[value].ewm(alpha0.3).mean()局部回归平滑Loess这是一种更高级的非参数平滑方法。它对每个点用一个加权的最小二乘回归来拟合其附近的数据点权重随着距离增加而减小。STL分解中的“Loess”指的就是它。Loess能很好地适应序列的局部形状平滑异常值的同时尽可能保留真实趋势。可以使用statsmodels的lowess函数但更常见的是将其嵌入在STL分解的框架中。实操心得对于明显的瞬时脉冲噪声比如一个尖峰我通常首选用滚动中位数替换因为中位数对异常值本身就不敏感。对于一段短期的、小幅的波动可以考虑使用指数平滑来削弱其影响。记住平滑本质上是一种低通滤波它在抹平异常的同时也会抹去一些真实的高频信号如突然的阶跃变化。所以如果那个“异常”可能代表一个真实的模式转变如产品升级后销量台阶式上升平滑它反而是有害的。4. 处理策略二基于模型预测的修正当时间序列具有明显的趋势性或季节性时我们可以利用序列自身的规律来“预测”异常点本该有的值。这是一种更“智能”的修正方式。4.1 利用序列自身规律ARIMA与回归模型核心思想是用一个不考虑异常点的“干净”模型去预测异常点时刻的值并用预测值替代异常观测值。构建基准模型首先你需要一个能较好拟合序列主流模式的模型。对于线性趋势和季节性的序列SARIMA季节性ARIMA是一个经典选择。对于更复杂的模式可以考虑使用线性回归加上傅里叶项来拟合季节性和趋势。检测并屏蔽异常值用我们第二节的方法如STL残差分析初步找出异常点的索引。训练与预测在训练模型时暂时将这些异常点作为缺失值处理NaN让模型在不被这些异常点干扰的情况下进行拟合。然后用拟合好的模型去预测这些异常点时间戳对应的值。替换用预测值替换原始的异常值。# 概念性代码示例 import pandas as pd import numpy as np from statsmodels.tsa.statespace.sarimax import SARIMAX # 1. 假设df[value]是原始序列anomaly_idx是异常点索引列表 df_train df.copy() df_train.loc[anomaly_idx, value] np.nan # 将异常点设为NaN # 2. 拟合SARIMA模型参数需根据你的数据调整 # 注意statsmodels的SARIMAX可以处理内部缺失值 model SARIMAX(df_train[value], order(1,1,1), # (p,d,q) seasonal_order(1,1,1,12), # (P,D,Q,s) 月度数据s12 enforce_stationarityFalse, enforce_invertibilityFalse) model_fit model.fit(dispFalse) # 3. 获取所有时间点的拟合值包括对异常点位置的预测 fitted_values model_fit.get_prediction().predicted_mean # 4. 用拟合值替换原始异常值 df_corrected df.copy() df_corrected.loc[anomaly_idx, value] fitted_values.loc[anomaly_idx]这种方法的好处在于它利用了序列的整体模式进行“有理有据”的修正比简单的插值更贴合数据的内在规律。但它的缺点是依赖模型的准确性如果模型本身拟合不好修正值可能偏差很大。4.2 高级玩法预测区间与模型融合我们可以更进一步不只用点预测而是利用预测区间。使用预测区间作为边界许多模型如SARIMAX、Prophet在预测时可以给出置信区间例如95%的预测区间。我们可以将原始值与此区间进行比较。如果原始值落在区间之外则被视为异常并用预测均值进行修正如果落在区间内则保留原值。这增加了一个判断的缓冲带避免了过度修正。模型融合修正不要只依赖一个模型。可以分别用线性模型、树模型如LightGBM以时间特征和滞后项为输入、甚至简单的季节性朴素预测如用去年同期的值来预测异常点的值然后取这些预测值的中位数作为最终修正值。中位数能有效抵御某个单一模型预测失败带来的风险。踩坑提醒使用基于模型的方法时最大的陷阱是数据泄露。绝对不能用包含待修正异常点的完整序列去训练模型然后让这个模型去预测同一个异常点——这相当于让模型“提前看到了答案”。必须确保训练模型时异常点的影响已被隔离如设为NaN。此外对于序列开头和结尾的异常点由于缺乏足够的前后文进行模型拟合修正效果可能较差需要结合其他方法或业务判断。5. 处理策略三分解与重构这是处理时间序列异常值非常强大且主流的一类方法其核心哲学是“分而治之”。通过分解我们将混杂着异常值的原始序列拆分成几个更容易理解的成分在成分层面对异常进行处理最后再组合回去。5.1 STL分解鲁棒分解的利器我们之前提到用STL分解来识别异常它同样也是处理异常的工具。STL分解的robust参数是关键。当robustTrue时分解过程会使用鲁棒性权重在迭代拟合趋势和季节成分时降低异常残差点的影响力。这相当于在分解过程中就自动对异常值进行了“平滑”处理。处理流程通常是用robustTrue参数进行STL分解得到趋势(T)、季节(S)和残差(R)分量。对残差分量R应用异常检测如3-sigma法找出异常索引。方案A修正残差将识别出的异常残差值修正为0或某个正常范围的值得到修正后的残差R_corrected。然后重构序列Corrected_Series T S R_corrected。方案B直接使用鲁棒分解结果由于使用了鲁棒分解趋势和季节成分受异常影响较小我们可以直接使用T S作为去除了异常和部分噪声的平滑序列。这本质上是将残差包含异常和随机噪声完全丢弃了。适用于预测任务因为很多模型假设残差是白噪声。from statsmodels.tsa.seasonal import STL import numpy as np # 鲁棒性STL分解 stl STL(df[value], period12, robustTrue) # robustTrue是核心 result stl.fit() trend result.trend seasonal result.seasonal resid result.resid # 检测残差中的异常 resid_mean, resid_std resid.mean(), resid.std() anomaly_idx np.where(np.abs(resid - resid_mean) 3 * resid_std)[0] # 方案A修正残差 resid_corrected resid.copy() resid_corrected.iloc[anomaly_idx] 0 # 将异常残差设为0即认为它完全由异常引起 series_corrected_A trend seasonal resid_corrected # 方案B丢弃残差得到平滑序列 series_corrected_B trend seasonal5.2 预测-分解-修正循环对于含有突刺型异常且对未来预测有影响的序列可以采用一种迭代式的方法用原始序列训练一个初始预测模型如ETS、Theta。用这个模型预测未来一段时间。将预测值与历史数据拼接形成一个“延长”的序列。对这个延长序列进行STL分解。由于未来部分是模型预测的通常比较平滑它可以帮助分解算法更稳定地估计历史序列末端的趋势成分减少边界效应。基于这个分解结果去修正历史序列中的异常值如用趋势季节成分替换原始值。用修正后的历史序列重新训练预测模型往往能得到更准确的预测。这种方法将异常值处理与预测模型的迭代优化结合了起来在实践中对于提升序列末端的预测稳定性特别有效。6. 处理策略四基于统计与距离的检测与修正这类方法不依赖于复杂的模型或分解而是基于统计学原理或数据点之间的距离关系来定义“异常”并通常采用“裁剪”或“缩尾”的方式进行修正。6.1 Sigma原则标准差法与MAD法3-Sigma原则假设数据服从正态分布那么99.7%的数据会落在均值±3个标准差的范围内。在此范围外的点被视为异常。这是最经典的方法。问题时间序列数据往往不服从正态分布且均值和标准差本身极易受异常值影响不够鲁棒。修正将超出[mean - 3*std, mean 3*std]的值直接裁剪Winsorize到这个边界上。MAD法Median Absolute Deviation中位数绝对偏差为了解决均值和标准差不鲁棒的问题使用中位数代替均值用MAD代替标准差。MAD median(|Xi - median(X)|)对于正态分布标准差σ ≈ 1.4826 * MAD。异常判断阈值通常设为[median - k * 1.4826 * MAD, median k * 1.4826 * MAD]k常取3或3.5。修正同样将超出阈值的值裁剪到阈值上。MAD法对异常值的鲁棒性远高于Sigma原则因为它基于中位数。在时间序列的全局异常检测中我通常更推荐使用MAD法。6.2 百分位法缩尾处理这是一种非常直观且不依赖于分布假设的方法。直接取序列的某个低分位如1%和高分位如99%作为正常值的边界。将小于1%分位数的值设置为1%分位数的值将大于99%分位数的值设置为99%分位数的值。这就是“缩尾”。# 百分位缩尾处理 lower_bound df[value].quantile(0.01) upper_bound df[value].quantile(0.99) df[value_winsorized] df[value].clip(lowerlower_bound, upperupper_bound)这种方法简单粗暴能确保处理后的数据绝对落在某个范围内。但它有两个明显缺点一是阈值的选择1%和99%比较主观二是它会改变数据的原始分布特别是当异常值比例不低时可能会扭曲真实的数据关系。6.3 基于距离的方法局部离群因子LOFLOF算法通过计算一个点的局部密度与其邻居点的局部密度之比来判断其是否为异常。比值显著大于1的点被认为是离群点。LOF的优点是可以发现局部异常并且不依赖于全局分布假设。在时间序列中我们不能直接将时间点作为特征输入LOF。通常需要构建一个“时间窗口特征”。例如对于每个时间点t取其前后k个点组成一个长度为(2k1)的向量[X(t-k), ..., X(t), ..., X(tk)]将这个向量作为该点的特征。然后对所有时间点的特征向量计算LOF分数。from sklearn.neighbors import LocalOutlierFactor import numpy as np # 构建滑动窗口特征 window_size 5 features [] for i in range(window_size, len(df)-window_size): features.append(df[value].iloc[i-window_size:iwindow_size1].values) features np.array(features) # 计算LOF lof LocalOutlierFactor(n_neighbors20, contamination0.05) # contamination是异常值比例估计 lof_labels lof.fit_predict(features) # 返回1正常或-1异常 # 找出异常点对应的原始时间索引 anomaly_indices np.where(lof_labels -1)[0] window_size # 注意索引偏移LOF计算量较大且需要谨慎选择窗口大小k和邻居数n_neighbors。它更适合于检测“上下文异常”即局部模式不一致的点。对于修正通常是将LOF识别出的异常点标记出来然后采用前文提到的插值或模型预测法进行修正。7. 处理策略五为异常值打上标签并非所有异常值都应该被修正或删除。在业务场景中很多“异常”背后是有明确原因的比如“双十一”的销量暴增、“春节”的工厂停工。这类异常是真实的业务事件如果粗暴地修正或平滑掉模型就学不到这些重要模式导致在未来类似事件发生时预测失败。更聪明的做法是识别它们并为它们创建特征标签。7.1 构建虚拟变量哑变量对于已知的、有明确发生时间的特殊事件如法定节假日、促销日、系统故障日我们可以直接创建二值虚拟变量0/1在对应日期标记为1。# 假设有一个促销日期列表 promo_dates [2023-06-18, 2023-11-11, 2024-01-01] df[is_promotion] 0 df.loc[df.index.isin(promo_dates), is_promotion] 1在后续构建预测模型如线性回归、LightGBM、甚至某些配置下的ARIMA时将这些虚拟变量作为外生回归变量加入模型。这样模型就能单独学习到这些特殊事件对目标值的影响。7.2 自动检测并生成事件标签对于历史上未知的、但通过算法检测出的显著异常点我们也可以将其转化为事件标签。例如用STL分解或LOF算法检测出异常点后不修改原始值而是生成一个新特征anomaly_flag。# 沿用之前STL检测异常的代码 df[anomaly_flag] 0 df.loc[anomaly_idx, anomaly_flag] 1在训练模型时同时使用原始序列value和异常标志anomaly_flag作为特征。对于树模型它可以学习到当anomaly_flag1时应该如何调整预测值。这相当于让模型自己去发现异常点的“修正规则”。进阶技巧你甚至可以区分异常的方向正向异常/负向异常和强度。例如计算异常残差的大小生成一个连续型特征anomaly_magnitude这能为模型提供更细粒度的信息。df[anomaly_magnitude] 0 df.loc[anomaly_idx, anomaly_magnitude] resid.iloc[anomaly_idx] # 使用残差值作为强度这种方法的巨大优势在于可解释性和业务结合度。你可以分析哪些事件标签的系数最大从而理解影响业务的关键因素。它保留了数据的完整性并将异常信息转化为模型可用的知识是处理“有信息量的异常”的最佳实践。8. 处理策略六使用对异常值鲁棒的模型如果我们觉得预处理太麻烦或者异常点的性质和位置非常不确定有没有一种“一劳永逸”的方法答案是使用对异常值本身就不敏感鲁棒的模型。这类模型在训练过程中就给异常值更低的权重或者使用不易受极端值影响的损失函数。8.1 分位数回归传统的回归模型如线性回归最小化均方误差MSE它对大误差给予极高的惩罚因此对异常值极其敏感。分位数回归则不同它预测的是目标变量的条件分位数如中位数。最小化绝对误差MAE的模型其预测目标就是中位数而中位数对异常值的鲁棒性远强于均值。以LightGBM为例你可以通过设置objectivequantile和alpha参数指定分位数0.5就是中位数来进行分位数回归。import lightgbm as lgb # 准备特征X和目标y时间序列特征工程后的数据 params { objective: quantile, alpha: 0.5, # 预测中位数 metric: mae, boosting_type: gbdt, num_leaves: 31, learning_rate: 0.05, } model lgb.LGBMRegressor(**params) model.fit(X_train, y_train)使用中位数预测作为最终输出模型预测结果受异常值的影响会小很多。你还可以同时训练多个分位数模型如0.1, 0.5, 0.9来得到预测区间。8.2 使用Huber Loss或Quantile Loss的深度学习模型在训练LSTM、Transformer等深度学习模型进行时间序列预测时我们可以放弃MSE转而使用更鲁棒的损失函数。Huber Loss可以看作是MAE和MSE的结合。当误差小于某个阈值δ时它使用平方项像MSE当误差大于δ时它使用线性项像MAE。这使得它对小的误差保持平滑性对大的异常误差又不过度敏感。# 在TensorFlow/Keras中的示例 import tensorflow as tf model.compile(optimizeradam, losstf.keras.losses.Huber(delta1.0))Quantile Loss直接优化指定的分位数。quantile_loss max(α * (y_true - y_pred), (α - 1) * (y_true - y_pred))。当α0.5时它就是MAE。通过调整α可以让模型关注预测分布的不同位置。8.3 树模型的内在鲁棒性基于决策树的集成模型如Random Forest, Gradient Boosting, LightGBM, XGBoost本身对异常值就有一定的鲁棒性。因为树的分裂是基于数据排序和分桶而不是像线性回归那样基于距离的平方。一个极端的异常值通常只会影响它所在的那个叶子节点而不会像在线性模型中那样“拉扯”整个回归线。但是这并不意味着树模型完全免疫。如果异常值非常多或者特征工程中包含了与异常值高度相关的特征树模型仍然可能受到影响。通常树模型 合理的特征工程如加入时间滞后特征、滚动统计特征 分位数损失/目标能构成一个异常鲁棒性很强的预测管道。模型选择建议如果你的数据中异常值很多且难以清晰界定和标注那么将“使用鲁棒模型”作为基线方案是明智的。可以先尝试LightGBM Quantile Regression它速度快、效果好。如果追求极致性能且数据量足够再考虑使用Huber Loss的LSTM/Transformer。记住没有银弹最好的结果往往来自“鲁棒预处理”“鲁棒模型”的组合拳。9. 实战流程与选型指南如何组合运用这些方法面对一个具体的时间序列预测任务我们该如何选择和组合这些方法呢根据我的经验一个系统化的处理流程如下你可以把它当作一个检查清单第一步探索性数据分析与异常初筛可视化绘制时序图、滚动均值/标准差带、分布直方图。业务沟通列出所有已知的特殊事件日期节假日、促销、故障日。初步标记使用STL分解robustTrue分析残差结合业务事件列表标记出第一批“嫌疑”异常点。区分“疑似错误”和“已知事件”。第二步制定处理策略根据异常点的性质和业务目标决定处理路径路径A确认为错误/无信息噪声点异常、数量少使用前后邻居的滚动中位数或线性插值直接替换。点异常、数量多或影响建模采用基于模型预测的修正。用SARIMA或线性回归屏蔽异常点训练预测并替换。或者使用STL分解重构方案A或B。集体异常如一段平直线这段数据可能完全无效。考虑将其整体视为缺失片段使用更复杂的缺失值填补方法如动态模型插补或者如果片段不长直接使用前后数据插值。路径B确认为有业务意义的特殊事件创建虚拟变量这是首选。为每个已知事件类型创建0/1标志。自动生成事件标签对于算法检测出但未知原因的重大异常生成anomaly_flag和anomaly_magnitude特征。保留原始值在序列中保留这些点不变让模型结合事件特征去学习。路径C情况复杂难以清晰界定采用鲁棒模型直接使用以MAE或Huber Loss为损失函数的模型或进行分位数回归。温和的缩尾处理使用99.5%和0.5%分位数进行轻微的缩尾削弱极端值的影响而非完全移除。第三步效果验证与迭代处理完异常值后绝不能直接丢给模型了事必须验证处理效果。再次可视化将处理前后的序列绘制在同一张图上观察异常点是否被合理修正趋势季节模式是否被保留。建模对比这是一个黄金标准。用处理前和处理后的数据在同一个简单的基准模型如季节性朴素预测、线性回归上跑一次验证集预测。比较MAE、MAPE等鲁棒性指标。如果处理后数据的预测精度有显著提升说明处理是有效的。残差分析用处理后的数据训练一个简单模型分析其预测残差。理想的残差应该近似白噪声没有明显的自相关和异方差。如果残差中仍有明显的、有规律的异常说明还有未处理好的模式。选型速查表场景特点推荐方法理由与注意事项少量、孤立的明显错误点滚动中位数/线性插值替换简单快速对整体序列影响小。具有强趋势/季节性的序列中的异常STL分解重构 或 基于SARIMA的预测修正利用序列自身规律修正更合理。需防止数据泄露。已知的特殊事件促销、假期创建虚拟变量/事件标签保留业务信息提升模型可解释性和预测能力。异常点多、分布未知、情况复杂使用鲁棒模型分位数回归、Huber Loss避免繁琐且可能出错的预处理让模型自适应。全局性极端值需要快速处理MAD法缩尾 或 百分位法缩尾快速将数据限制在合理范围。会改变数据分布需谨慎。检测局部异常模式LOF算法能发现传统方法难以发现的上下文异常。计算成本较高。最后也是最重要的心得没有“最好”的方法只有“最合适”的方法。在实际项目中我通常会尝试2-3种不同的处理策略并通过严格的建模对比来选择效果最好的那一种。时间序列异常值处理既是科学也是艺术它要求我们不断地在数据、算法和业务认知之间寻找最佳平衡点。
返回列表