与异常检测缺失值时间序列的STL分解

问题描述 投票:15回答:2

我想在时间序列上的一些缺失观测气候数据来检测异常值。搜索,我发现许多可用的方法网页。其中,STL分解似乎吸引力,去除趋势和季节性成分和研究其余的意义。阅读STL: A Seasonal-Trend Decomposition Procedure Based on Loess,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与缺失值,或者如果您认为我的选择是在方法论上不健全,或者如果你有什么更好的建议。我在场上很新和的(貌似...)相关信息的堆不堪重负。

r statistics time-series na stl-decomposition
2个回答
19
投票

stl年初,我们find

x <- na.action(as.ts(x))

不久之后

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

也就是说,stl预计xts对象后na.action(as.ts(x))(否则period == 1)。让我们检查na.omit和第一na.exclude

显然,在getAnywhere("na.omit.ts")结束时,我们发现

if (any(is.na(object))) 
    stop("time series contains internal NAs")

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

    attr(omit, "class") <- "exclude"

这是一个不同的情况。如上所述,预计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")

作品。在一般情况下,stlNA值(即与na.action = na.pass),它崩溃了Fortran中(见完整的源代码here)更深层次的工作:

z <- .Fortran(C_stl, ...

替代na.new不愉快:

  • na.contaguous - 发现非缺失值的最长连续伸展在时间序列对象。
  • na.approx,从na.locf或一些其他插补功能zoo
  • 不知道这一个,但另一个Fortran的实现可以为Python here找到。人们可以使用Python的可能是一些修改后,从源代码安装R,万一这个模块真正让缺失值。

正如我们在paper看到,有没有遗漏值(如在一开始接近他们)一些简单的过程,它可以调用stl之前被应用到时间序列。因此,在考虑到original implementation是我曾经想比全新的实现一些其他的替代品非常冗长。

更新:具有当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, "%"))))
}

nottem,240个观测

stlCheck(nottem, s.window = 4, t.window = 50, t.jump = 1)

co2,468个观察

stlCheck(log(co2), s.window = 21)

mdeaths,72个观测

stlCheck(mdeaths, s.window = "per")

直观我们确实看到趋势的情况下1和3的一些差异,但这些差异都在1非常小,在3考虑样品尺寸(72)也令人满意。


6
投票

意识到这是一个老问题,但认为我会更新,因为有R中可用的新stl包叫做stlplusHere is its homepage on github。您可以从CRAN与install.packages("stlplus")从GitHub与devtools::install_github("hafen/stlplus")安装或直接。

© www.soinside.com 2019 - 2024. All rights reserved.