具有缺失值的时间序列的STL分解用于异常检测

eff*_*pav 15 statistics r time-series na stl-decomposition

我试图在一些时间序列的气候数据中检测到异常值,但缺少一些观察结果.在网上搜索我找到了许多可用的方法.其中,从消除趋势和季节性成分以及研究其余部分的意义上来说,stl分解似乎很有吸引力.阅读STL:季节性趋势分解过程基于黄土,stl似乎可以灵活地确定分配变异性的设置,不受异常值的影响,并且可以应用尽管缺失值.但是,尝试将其应用于R,经过四年的观察并根据http://stat.ethz.ch/R-manual/R-patched/library/stats/html/stl.html定义所有参数,我遇到错误:

时间序列包含内部NA

什么时候na.action = na.omit,和

系列不是周期性的或少于两个周期

na.action = na.exclude.

我仔细检查了频率是否正确定义.我在博客中看到了相关问题,但没有找到任何可以解决此问题的建议.是否无法在缺少值的系列中应用stl?我非常不愿意插入它们,因为我不想引入(并因此检测......)工件.出于同样的原因,我不知道使用ARIMA方法是否可行(如果缺失值仍然存在问题).

如果您知道在缺失值的系列中应用stl的方法,或者如果您认为我的选择在方法上不合理,或者您有任何更好的建议,请分享.我在这个领域很新,并且被大量的(看似......)相关信息所震撼.

Jul*_*ora 19

stl我们找到的开始

x <- na.action(as.ts(x))
Run Code Online (Sandbox Code Playgroud)

不久之后

period <- frequency(x)
if (period < 2 || n <= 2 * period) 
    stop("series is not periodic or has less than two periods")
Run Code Online (Sandbox Code Playgroud)

也就是说,stl期望在(否则)之后x成为ts对象.让我们检查和第一.na.action(as.ts(x))period == 1na.omitna.exclude

显然,在getAnywhere("na.omit.ts")我们找到的最后

if (any(is.na(object))) 
    stop("time series contains internal NAs")
Run Code Online (Sandbox Code Playgroud)

这很简单,没有什么可以做(na.omit不排除NAsts对象).现在getAnywhere("na.exclude.default")排除NA观察,但返回类的对象exclude:

    attr(omit, "class") <- "exclude"
Run Code Online (Sandbox Code Playgroud)

这是另一种情况.如上所述,stl期望na.action(as.ts(x))ts,但是na.exclude(as.ts(x))属于阶级exclude.

因此,如果一个人对NAs排除感到满意,那么例如

nottem[3] <- NA
frequency(nottem)
# [1] 12
na.new <- function(x) ts(na.exclude(x), frequency = 12)
stl(nottem, na.action = na.new, s.window = "per")
Run Code Online (Sandbox Code Playgroud)

作品.一般情况下,stl不使用NA值(即with na.action = na.pass),它在Fortran中崩溃更深(请参阅此处的完整源代码):

z <- .Fortran(C_stl, ...
Run Code Online (Sandbox Code Playgroud)

替代方案na.new并不令人愉快:

  • na.contaguous - 在时间序列对象中查找最长的连续延伸的非缺失值.
  • na.approx,na.locf来自zoo或其他一些插值函数.
  • 不确定这个,但在这里可以找到Python的另一个Fortran实现.如果这个模块确实允许缺少值,可以在一些修改之后使用可能从源安装R的Python.

正如我们在文章中所看到的,没有一些简单的过程可以用于缺失值(比如在最开始时对它们进行近似),这些过程可以应用于调用之前的时间序列stl.因此,考虑到原始实现非常冗长的事实,我会考虑除了全新实现之外的其他一些替代方案.

更新:一个在许多方面的选择相当具有最佳的时候NAs可能是na.approxzoo,让我们检查它的性能,即比较的结果stl具有一定数目时设置完整的数据和结果NAs,利用na.approx.我使用MAPE作为准确度的度量,但仅用于趋势,因为季节性成分和余数过零并且会使结果失真.职位NAs是随机选择的.

library(zoo)
library(plyr)
library(reshape)
library(ggplot2)
mape <- function(f, x) colMeans(abs(1 - f / x) * 100)

stlCheck <- function(data, p = 3, ...){
  set.seed(20130201)
  pos <- lapply(3^(0:p), function(x) sample(1:length(data), x))
  datasetsNA <- lapply(pos, function(x) {data[x] <- NA; data})
  original <- data.frame(stl(data, ...)$time.series, stringsAsFactors = FALSE)
  original$id <- "Original"
  datasetsNA <- lapply(datasetsNA, function(x) 
    data.frame(stl(x, na.action = na.approx, ...)$time.series, 
               id = paste(sum(is.na(x)), "NAs"), 
               stringsAsFactors = FALSE))
  stlAll <- rbind.fill(c(list(original), datasetsNA))
  stlAll$Date <- time(data)
  stlAll <- melt(stlAll, id.var = c("id", "Date"))
  results <- data.frame(trend = sapply(lapply(datasetsNA, '[', i = "trend"), mape, original[, "trend"]))
  results$id <- paste(3^(0:p), "NAs")
  results <- melt(results, id.var = "id")
  results$x <- min(stlAll$Date) + diff(range(stlAll$Date)) / 4
  results$y <- min(original[, "trend"]) + diff(range(original[, "trend"])) / (4 * p) * (0:p)
  results$value <- round(results$value, 2)
  ggplot(stlAll, aes(x = Date, y = value, colour = id, group = id)) + geom_line() + 
    facet_wrap(~ variable, scales = "free_y") + theme_bw() +
    theme(legend.title = element_blank(), strip.background = element_rect(fill = "white")) + 
    labs(x = NULL, y = NULL) + scale_colour_brewer(palette = "Set1") +
    lapply(unique(results$id), function(z)
      geom_text(data = results, colour = "black", size = 3,
                aes(x = x, y = y, label = paste0("MAPE (", id, "): ", value, "%"))))
}
Run Code Online (Sandbox Code Playgroud)

nottem,240观察

stlCheck(nottem, s.window = 4, t.window = 50, t.jump = 1)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

co2,468个观察

stlCheck(log(co2), s.window = 21)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

mdeaths,72个观察

stlCheck(mdeaths, s.window = "per")
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

在视觉上我们确实看到了病例1和3中趋势的一些差异.但是这些差异在1中非常小,并且在考虑样本大小的情况下在3中也令人满意(72).


sfj*_*jac 6

意识到这是一个老问题,但我想我会更新,因为stlR中有一个更新的包可用stlplus.这是github上的主页.您可以使用install.packages("stlplus")或直接从github 安装CRAN devtools::install_github("hafen/stlplus").