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

R 基于时间窗的不规则时间序列滚动函数优化,r,time-series,zoo,data.table,R,Time Series,Zoo,Data.table,是否有某种方法可以使用rollapply(来自zoopackage或类似的)优化函数(rollmean,rollmedian等)来计算基于时间窗口的滚动函数,而不是基于大量观察的滚动函数?我想要的很简单:对于不规则时间序列中的每个元素,我想要计算一个具有N天窗口的滚动函数。也就是说,窗口应包括当前观测前N天内的所有观测。时间序列也可能包含重复项 下面是一个例子。鉴于以下时间序列: date value 1/11/2011 5 1/11/2011 4 1/1

是否有某种方法可以使用rollapply(来自
zoo
package或类似的)优化函数(
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
具有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

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

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

图书馆(动物园)
图书馆(dplyr)
#创建一个长时间序列
启动1)rollapply未检查速度,但如果没有任何日期出现超过
max.dup
的情况,则必须是最后5*max.dup条目包含最后5天,以便将下面显示的单行函数
fn
传递给
rollapplyr
即可:

k <- 5

dates <- as.numeric(DF$date)
values <- DF$value

max.dup <- max(table(dates))

fn <- function(ix, d = dates[ix], v = values[ix], n = length(ix)) median(v[d >= d[n]-k])

rollapplyr(1:nrow(DF), max.dup * k, fn, partial = TRUE)
## [1] 5.0 4.5 4.0 1.0 0.5 0.0 0.0 0.0 2.5 3.0
给予:

res$median
## [1] 5.0 4.5 4.0 1.0 0.5 0.0 0.0 0.0 2.5 3.0
注意:我们将其用于
DF

 Lines <- "
      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
"
DF <- read.table(text = Lines, header = TRUE)
DF$date <- as.Date(DF$date, format = "%d/%m/%Y")

行大多数答案建议插入NA以使时间序列规则化。
但是,在时间序列较长的情况下,这可能会很慢。此外,它不适用于不能与NA一起使用的函数

rollapply(zoo包)的宽度参数可以是一个列表(有关详细信息,请参阅rollapply的帮助)。基于此,我编写了一个函数,该函数创建一个列表,与rollapply一起用作宽度参数。如果移动窗口是基于时间而不是基于索引的,则该函数可提取不规则动物园对象的索引。因此,zoo对象的索引应该是实际时间

# Create a zoo object where index represents time (e.g. in seconds) 

d <- zoo(c(1,1,1,1,1,2,2,2,2,2,16,25,27,27,27,27,27,31),     
         c(1:5,11:15,16,25:30,31))

# Create function 

createRollapplyWidth = function(zoodata, steps, window ){   

  mintime =  min(time(zoodata))     

  maxtime =  max(time(zoodata)) 

  spotstime = seq(from = mintime , to = maxtime, by = steps)

  spotsindex = list() 

    for (i in 1:length(spotstime)){
    spotsindex[[i]] =  as.numeric(which(spotstime[i]  <=  time(zoodata) & time(zoodata) < spotstime[i] + window))}

  rollapplywidth = list()
    for (i in 1:length(spotsindex)){
    if (!is.na(median(spotsindex[[i]])) ){ 
      rollapplywidth[[round(median(spotsindex[[i]]))]] = spotsindex[[i]] - round(median(spotsindex[[i]]))}
  }
  return(rollapplywidth)
  }


# Create width parameter for rollapply using function

rollwidth =  createRollapplyWidth(zoodata = d, steps = 5, window = 5) 

# Use parameter in rollapply 

result = rollapply(d, width = rollwidth , FUN =  sum, na.rm = T) 
result
#创建一个索引表示时间的zoo对象(例如,以秒为单位)
d自版本1.9.8(2016年11月25日)起,已获得执行非等连接的能力,可在此处使用

OP要求

对于不规则时间序列中的每个元素,我想计算 具有N天窗口的滚动功能。也就是说,窗口应该 包括当前日期前N天内的所有观察结果 观察。时间序列也可能包含重复项

请注意,OP已要求在当前观测前N天内包含所有观测结果。这与在当前日期前N天内请求所有观测不同

对于后者,我希望2011年11月1日的
有一个值,即
中值(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

为完整起见,日间滚动窗口的可能解决方案可以是:

setDT(DT)[.(ud = unique(date), ld = unique(date) - n_days), on = .(date <= ud, date >= ld), 
   median(as.double(value)), by = .EACHI]
资料
库(data.table)
DT我建议使用经过优化的包来执行本主题中要求的操作。有关进一步的说明,请转到中的“窗口(取决于日期)”部分

要解决您的任务,可以使用
runner
函数,该函数可以在运行的windows中执行任何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

使用
rollapply
无法做到这一点。您可以使用
窗口
滚动您自己的函数(双关语)。这个问题和答案有帮助吗<代码>滚动应用
如果您使用
中值
作为乐趣,则通过调用
滚动中值
进行“欺骗”。比较:
system.time(rollappy(runif(100000),5,函数(x)中间值(x))
system.time(rollappy(runif(100000),5,中间值))
(前者慢30倍)。如果你想在没有“作弊”的情况下达到与
rollapply
相当的速度,我可以提供一些解决方案。此外,
rollmedian
也会“作弊”,因为它需要奇数观察,所以很明显,它只是定义了一个“中间”值的索引,这与您正在尝试的操作相比微不足道。请参见此答案()对于基于时间窗口的快速Rcpp rollmean函数。是否可以通过填充NA使时间序列规则化,然后对其应用固定大小的滚动窗口?
[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]
         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")]
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
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