如何使用 data.table 获取滚动产品

How do I take a rolling product using data.table

dt <- data.table(x=c(1, .9, .8, .75, .5, .1))
dt
      x
1: 1.00
2: 0.90
3: 0.80
4: 0.75
5: 0.50
6: 0.10

对于每一行,如何获得该行和接下来两行的 x 的乘积?

      x Prod.3
1: 1.00 0.7200
2: 0.90 0.5400
3: 0.80 0.3000
4: 0.75 0.0375
5: 0.50     NA
6: 0.10     NA

更一般地说,对于每一行,我如何获得该行和接下来 n 行的 x 的乘积?

你可以试试

library(zoo)
rollapply(dt, 3, FUN = prod)
          x
[1,] 0.7200
[2,] 0.5400
[3,] 0.3000
[4,] 0.0375

匹配预期输出

dt[, Prod.3 :=rollapply(x, 3, FUN=prod, fill=NA, align='left')]

这里有两种方法..尽管不是最有效的实施方式:

require(data.table)
N = 3L
dt[, prod := prod(dt$x[.I:(.I+N-1L)]), by=1:nrow(dt)]

另一个使用 embed():

tmp = apply(embed(dt$x, N), 1, prod)
dt[seq_along(tmp), prod := tmp]

基准:

set.seed(1L)
dt = data.table(x=runif(1e6))
zoo_fun <- function(dt, N) {
    rollapply(dt$x, N, FUN=prod, fill=NA, align='left')
}

dt1_fun <- function(dt, N) {
    dt[, prod := prod(dt$x[.I:(.I+N-1L)]), by=1:nrow(dt)]
    dt$prod
}

dt2_fun <- function(dt, N) {
    tmp = apply(embed(dt$x, N), 1L, prod)
    tmp[1:nrow(dt)]
}

david_fun <- function(dt, N) {
    Reduce(`*`, shift(dt$x, 0:(N-1L), type="lead"))
}

system.time(ans1 <- zoo_fun(dt, 3L))
#    user  system elapsed 
#   8.879   0.264   9.221 
system.time(ans2 <- dt1_fun(dt, 3L))
#    user  system elapsed 
#  10.660   0.133  10.959
system.time(ans3 <- dt2_fun(dt, 3L))
#    user  system elapsed 
#   1.725   0.058   1.819 
system.time(ans4 <- david_fun(dt, 3L))
#    user  system elapsed 
#   0.009   0.002   0.011 

all.equal(ans1, ans2) # [1] TRUE
all.equal(ans1, ans3) # [1] TRUE
all.equal(ans1, ans4) # [1] TRUE

这是另一个使用 data.table::shift 结合 Reduce 的可能版本(根据@Aruns 评论)

library(data.table) #v1.9.6+
N <- 3L
dt[, Prod3 := Reduce(`*`, shift(x, 0L:(N - 1L), type = "lead"))]

shift 是向量化的,这意味着它可以根据传递给 n 参数的向量一次创建多个新列。然后,Reduce 基本上立即将 * 应用于所有元素的向量。

现在data.table有快速滚动功能。所以@Mamoun Benghezal 的方法可以用作

dt[, Prod.3 := frollapply(x, 3, FUN=prod, fill=NA, align='left')]

这非常快,但不如@David Arenburg 的功能快。使用@Arun 的基准:

set.seed(1L)
dt = data.table(x=runif(1e6))

froll_fun <- function(dt, N) {
    frollapply(dt$x, N, FUN = prod, fill = NA, align = 'left')
}

system.time(ans5 <- froll_fun(dt, 3L))
#  user  system elapsed 
# 0.191   0.000   0.191