通过基于时间的窗口优化不规则时间序列的滚动函数

vsa*_*dra 26 r time-series zoo

是否有某种方式使用rollapply(从zoo包装或类似的东西)优化功能(rollmean,rollmedian等)来计算与基于时间窗口的滚动功能,而不是一个基于的若干意见?我想要的很简单:对于不规则时间序列中的每个元素,我想计算一个带有N天窗口的滚动函数.也就是说,窗口应包括当前观察前N天的所有观察结果.时间序列也可能包含重复项.

以下是一个例子.鉴于以下时间序列:

      date  value
 1/11/2011      5
 1/11/2011      4
 1/11/2011      2
 8/11/2011      1
13/11/2011      0
14/11/2011      0
15/11/2011      0
18/11/2011      1
21/11/2011      4
 5/12/2011      3
Run Code Online (Sandbox Code Playgroud)

具有5天窗口的滚动中位数(右侧对齐)应导致以下计算:

> c(
    median(c(5)),
    median(c(5,4)),
    median(c(5,4,2)),
    median(c(1)),
    median(c(1,0)), 
    median(c(0,0)),
    median(c(0,0,0)),
    median(c(0,0,0,1)),
    median(c(1,4)),
    median(c(3))
   )

 [1] 5.0 4.5 4.0 1.0 0.5 0.0 0.0 0.0 2.5 3.0
Run Code Online (Sandbox Code Playgroud)

我已经找到了一些解决方案,但它们通常很棘手,通常意味着很慢.我设法实现了自己的滚动函数计算.问题是,对于非常长的时间序列,中位数(rollmedian)的优化版本可以产生巨大的时间差,因为它考虑了窗口之间的重叠.我想避免重新实现它.我怀疑rollapply参数有一些技巧可以使它工作,但我无法弄明白.在此先感谢您的帮助.

Uwe*_*Uwe 5

从v1.9.8版本开始(2016年11月25日,CRAN),已获得执行联接的能力,可在此处使用。

OP已要求

对于不规则时间序列中的每个元素,我想计算一个具有N天窗的滚动函数。即,该窗口应包括当前观测之前N天之前的所有观测。时间序列也可能包含重复项。

请注意,OP已要求在当前观察之前的N天之内包括所有观察。要求在当天之前N天之前进行所有观察的情况有所不同。

对于后者,我希望为取一个1/11/2011,即median(c(5, 4, 2))= 4。

显然,OP希望基于观察的滚动窗口限于N天。因此,非等额联接的联接条件也必须考虑行号。

library(data.table)
n_days <- 5L
setDT(DT)[, rn := .I][
  .(ur = rn, ud = date, ld = date - n_days), 
  on = .(rn <= ur, date <= ud, date >= ld),
  median(as.double(value)), by = .EACHI]$V1
Run Code Online (Sandbox Code Playgroud)
[1] 5.0 4.5 4.0 1.0 0.5 0.0 0.0 0.0 2.5 3.0
Run Code Online (Sandbox Code Playgroud)

为了完整起见,基于的滚动窗口的可能解决方案可能是:

setDT(DT)[.(ud = unique(date), ld = unique(date) - n_days), on = .(date <= ud, date >= ld), 
   median(as.double(value)), by = .EACHI]
Run Code Online (Sandbox Code Playgroud)
         date       date  V1
1: 2011-11-01 2011-10-27 4.0
2: 2011-11-08 2011-11-03 1.0
3: 2011-11-13 2011-11-08 0.5
4: 2011-11-14 2011-11-09 0.0
5: 2011-11-15 2011-11-10 0.0
6: 2011-11-18 2011-11-13 0.0
7: 2011-11-21 2011-11-16 2.5
8: 2011-12-05 2011-11-30 3.0
Run Code Online (Sandbox Code Playgroud)

数据

library(data.table)
DT <- fread("      date  value
 1/11/2011      5
 1/11/2011      4
 1/11/2011      2
 8/11/2011      1
13/11/2011      0
14/11/2011      0
15/11/2011      0
18/11/2011      1
21/11/2011      4
 5/12/2011      3")[
   # coerce date from character string to integer date class
   , date := as.IDate(date, "%d/%m/%Y")]
Run Code Online (Sandbox Code Playgroud)


GoG*_*nzo 5

我建议使用经过优化的运行程序包来执行本主题中请求的操作。请参阅文档中的Windows 取决于日期部分,以获取进一步说明。

为了解决您的任务,可以使用runner可以在运行窗口中执行任何 R 函数的函数。这里单行:

df <- read.table(
  text = "date  value
   2011-11-01      5
   2011-11-01      4
   2011-11-01      2
   2011-11-08      1
   2011-11-13      0
   2011-11-14      0
   2011-11-15      0
   2011-11-18      1
   2011-11-21      4
   2011-12-05      3", header = TRUE, colClasses = c("Date", "integer"))

library(runner)
runner(df$value, k = 5, idx = df$date, f = median)
[1] 5.0 4.5 4.0 1.0 0.0 0.0 0.0 0.0 2.5 3.0
Run Code Online (Sandbox Code Playgroud)

PS 应注意的是 5 天窗口而[i-4, i-3, i-2, i-1, i]不是(i-5):i(6 天窗口)。下面的插图可以更好地解释这个概念。
我已经在 5 天的窗口中做了示例,但如果想按照 OP 的要求重现结果,可以指定 6 天的窗口:

运行日期窗口

identical(
  runner(df$value, k = 6, idx = df$date, f = median),
  c(5.0, 4.5, 4.0, 1.0, 0.5, 0.0, 0.0, 0.0, 2.5, 3.0)
)
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)


Fel*_*lix 0

这是我对这个问题的修改。如果这种方式达到了你想要的(我不知道它在速度方面是否令人满意),我可以将其写为更详细的答案(即使它是基于@rbatt的想法)。

library(zoo)
library(dplyr)

# create a long time series
start <- as.Date("1800-01-01")
end <- as.Date(Sys.Date())

df <- data.frame(V1 = seq.Date(start, end, by = "day"))
df$V2 <- sample(1:10, nrow(df), replace = T)

# make it an irregular time series by sampling 10000 rows
# including allowing for duplicates (replace = T)
df2 <- df %>% 
  sample_n(10000, replace = T)

# create 'complete' time series & join the data & compute the rolling median
df_rollmed <- data.frame(V1 = seq.Date(min(df$V1), max(df$V1), by = "day")) %>% 
  left_join(., df2) %>% 
  mutate(rollmed = rollapply(V2, 5, median, na.rm = T, align = "right", partial = T)) %>% 
  filter(!is.na(V2)) # throw out the NAs from the complete dataset
Run Code Online (Sandbox Code Playgroud)