You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

疑问点

  1. 在此场景下,为何locate.outliers()检测出的异常值更多?
  2. 这两个函数在残差处理、临界值(cval)或异常值类型的处理上是否存在内部差异?我已确认输出的ARIMA模型与cval参数一致。
  3. 若需使两者输出对齐,应进行哪些调整?

解答

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 06:23:23