为何R中tsoutliers包的locate.outliers()比tso()检测更多异常值?
关于tsoutliers包中locate.outliers()与tso()结果差异的疑问
我正在使用R语言的tsoutliers包进行时间序列异常值检测,发现locate.outliers()和tso()在输入参数看似一致的情况下,检测出的异常值结果(异常值表格)存在差异,且locate.outliers()检测出的异常值数量更多。
使用的代码如下:
# Using locate.outliers() m_opt <- Arima(x, order = c(8, 1, 0), seasonal = list(order = c(1, 1, 1), period = 7), xreg = fourier(x, K = 3)) resid <- residuals(m_opt) # residuals pars <- coefs2poly(m_opt) # collapses the polynomials of an ARIMA model outliers <- locate.outliers(resid, pars, types = c("TC", "AO", "LS"), cval = 3.77) print(outliers) # result # Using tso() tso_opt <- tso(x, types = c("TC", "AO", "LS"), xreg = fourier(x, K = 3), tsmethod = "arima", args.tsmethod = list(order = c(8, 1, 0), seasonal = list(order = c(1, 1, 1), period = 7)), cval = 3.77) print(tso_opt$outliers) # result
疑问点
- 在此场景下,为何
locate.outliers()检测出的异常值更多? - 这两个函数在残差处理、临界值(cval)或异常值类型的处理上是否存在内部差异?我已确认输出的ARIMA模型与cval参数一致。
- 若需使两者输出对齐,应进行哪些调整?
解答
1. 为何locate.outliers()检测出的异常值更多?
核心差异在于检测逻辑的本质不同:
locate.outliers()是基于固定初始ARIMA模型的残差,一次性识别所有符合阈值的异常值,完全不考虑异常值对模型参数的影响,属于“静态”检测逻辑。tso()采用迭代检测-修正-重拟合的动态流程:每检测到一个异常值,就会将该异常值对应的虚拟变量加入模型重新拟合ARIMA,再基于修正后的残差检测下一个异常值。已识别的异常值会被模型“解释”,后续残差更接近真实随机误差,很多在初始残差中看似显著的边缘异常值,在修正后会不再符合检测标准,最终检测到的数量更少。
2. 残差处理、临界值、异常值类型的内部差异
- 残差处理:
locate.outliers()使用的是未修正的初始ARIMA模型残差,残差中包含所有异常值的影响,更容易触发检测阈值。tso()每次迭代都会更新残差:将已检测异常值的虚拟变量纳入模型,重新拟合后计算残差,残差是经过异常值校正后的结果,噪声更低。
- 临界值(cval):
虽然设置了相同的cval,但tso()在迭代过程中会自动进行多重假设检验校正(如Bonferroni校正),降低误判概率;而locate.outliers()直接使用给定的cval,没有校正,对边缘异常值的容忍度更高。 - 异常值类型处理:
两者对TC(暂时变化)、AO(加性异常值)、LS(水平漂移)的定义完全一致,但tso()在迭代中会优先筛选影响更大的异常值,后续检测会排除已被修正的异常值干扰;locate.outliers()则一次性识别所有满足阈值的异常值,不考虑异常值之间的相互影响。
3. 如何让两者输出对齐?
有两种可行方案:
- 方案一:让tso()采用静态检测逻辑
在调用tso()时设置iter=0,关闭迭代重拟合流程,使其仅基于初始ARIMA模型的残差检测一次异常值,和locate.outliers()的逻辑对齐。修改后的代码如下:tso_opt <- tso(x, types = c("TC", "AO", "LS"), xreg = fourier(x, K = 3), tsmethod = "arima", args.tsmethod = list(order = c(8, 1, 0), seasonal = list(order = c(1, 1, 1), period = 7)), cval = 3.77, iter = 0) # 关闭迭代,仅做单次检测 print(tso_opt$outliers) - 方案二:让locate.outliers()模拟tso()的迭代流程
手动实现迭代循环:每次用locate.outliers()检测出异常值后,将对应的异常值虚拟变量(可通过outliers2xreg()生成)加入xreg,重新拟合ARIMA模型,再用新的残差和参数调用locate.outliers(),直到没有新的异常值被检测到。示例框架如下:current_xreg <- fourier(x, K = 3) detected_outliers <- data.frame() repeat { # 拟合当前模型 m <- Arima(x, order = c(8, 1, 0), seasonal = list(order = c(1, 1, 1), period = 7), xreg = current_xreg) resid <- residuals(m) pars <- coefs2poly(m) # 检测异常值 new_outliers <- locate.outliers(resid, pars, types = c("TC", "AO", "LS"), cval = 3.77) if (nrow(new_outliers) == 0) break # 合并已检测的异常值 detected_outliers <- rbind(detected_outliers, new_outliers) # 生成新的虚拟变量加入xreg outlier_xreg <- outliers2xreg(new_outliers, length(x)) current_xreg <- cbind(current_xreg, outlier_xreg) } print(detected_outliers)
内容的提问来源于stack exchange,提问作者chromanna
相关产品推荐
相关产品推荐

