【问题标题】:How do I take a rolling product using data.table如何使用 data.table 获取滚动产品
【发布时间】:2015-06-02 18:39:50
【问题描述】:
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 的乘积?

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    这是使用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 基本上将* 一次性应用于所有向量。

    【讨论】:

    • 这很酷.. 你可以这样做:Reduce(`*`, shift(dt, 0:2, type="lead"))
    • 令人难以置信的加速!是否可以使它在变量n 上工作?然后我可以在上面应用它:stackoverflow.com/q/21368245/2490497
    • @jan 我现在不在电脑前,所以明天再看看。也不确定“变量 n”是什么意思。
    • @DavidArenburg 制作滚动窗口向量而不是标量提供了一种编码自适应移动平均线的方法。
    【解决方案2】:

    这里有两种方法.. 虽然不是最有效的实现方式:

    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
    

    【讨论】:

    • 在这段代码中,只使用了一列。如何以 SDcols 方式传递多个列? dt[, prod := prod(dt$x[.I:(.I+N-1L)]), by=1:nrow(dt)].
    • @Arun 通过操作.N.I.SD,在data.table [.] 中可以实现的效果真的很惊人。不需要记住很多其他功能!
    【解决方案3】:

    你可以试试

    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')]
    

    【讨论】:

    • 这很慢(如 Arun 所示),但对于我的用例来说足够简单和快速。
    【解决方案4】:

    现在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 
    

    【讨论】:

      猜你喜欢
      • 2021-11-29
      • 1970-01-01
      • 2014-07-04
      • 1970-01-01
      • 2012-01-24
      • 2016-10-14
      • 2017-03-26
      相关资源
      最近更新 更多