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参数有一些技巧可以使它工作,但我无法弄明白.在此先感谢您的帮助.
从v1.9.8版本开始(2016年11月25日,CRAN),data.table已获得执行非等额联接的能力,可在此处使用。
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)
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
为了完整起见,基于天的滚动窗口的可能解决方案可能是:
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)
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
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)
我建议使用经过优化的运行程序包来执行本主题中请求的操作。请参阅文档中的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)
这是我对这个问题的修改。如果这种方式达到了你想要的(我不知道它在速度方面是否令人满意),我可以将其写为更详细的答案(即使它是基于@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)