【问题标题】:Consecutive/Rolling sums in a vector in RR中向量中的连续/滚动和
【发布时间】:2016-02-11 11:55:34
【问题描述】:

假设在 R 中我有以下向量:

[1 2 3 10 20 30]

如何执行一个操作,在每个索引处将 3 个连续元素相加,得到以下向量:

[6 15 33 60]

第一个元素 = 1+2+3,第二个元素 = 2+3+10 等等...?谢谢

【问题讨论】:

    标签: r rolling-computation rolling-sum


    【解决方案1】:

    你拥有的是一个向量,而不是一个数组。你可以使用 zoo 包中的rollapply 函数来获取你需要的东西。

    > x <- c(1, 2, 3, 10, 20, 30)
    > #library(zoo)
    > rollapply(x, 3, sum)
    [1]  6 15 33 60
    

    查看?rollapply,了解有关rollapply 的作用以及如何使用它的更多详细信息。

    【讨论】:

    • 谢谢,这正是我想要的。我将标记为答案(由于时间限制,现在不能做)。这是最快的方法吗?谢谢
    • 动物园包现在还包含rollsum 函数。 rollsum(x, 3)
    【解决方案2】:

    我整理了一个包来处理这些类型的“滚动”函数,它提供类似于zoorollapply 的功能,但在后端使用 Rcpp。在 CRAN 上查看 RcppRoll

    library(microbenchmark)
    library(zoo)
    library(RcppRoll)
    
    x <- rnorm(1E5)
    
    all.equal( m1 <- rollapply(x, 3, sum), m2 <- roll_sum(x, 3) )
    
    ## from flodel
    rsum.cumsum <- function(x, n = 3L) {
      tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1)
    }
    
    microbenchmark(
      unit="ms",
      times=10,
      rollapply(x, 3, sum),
      roll_sum(x, 3),
      rsum.cumsum(x, 3)
    )
    

    给我

    Unit: milliseconds
                     expr         min          lq      median         uq         max neval
     rollapply(x, 3, sum) 1056.646058 1068.867550 1076.550463 1113.71012 1131.230825    10
           roll_sum(x, 3)    0.405992    0.442928    0.457642    0.51770    0.574455    10
        rsum.cumsum(x, 3)    2.610119    2.821823    6.469593   11.33624   53.798711    10
    

    如果速度是一个问题,您可能会发现它很有用。

    【讨论】:

    • 很好,+1。这让我想知道:基于cumsum 的 Rcpp 会比 R 快得多吗?您的函数是否正确处理了 NA?
    • 对于 cumsum,可能不是——这已经是一个原始的,因此可能只是一个 C 循环。关于 NA 问题:这是一个很好的观点。他们现在的处理方式不一致。如果窗口中的元素之一为 NA,则大多数操作返回 NA,尽管 sd 返回 NaN。与 R 相比,min 和 max 忽略 NA。我猜 na.option 将是一个有用的参数。
    • @KevinUshey:非常感谢。这真的很快。
    【解决方案3】:

    如果速度是一个问题,您可以使用卷积过滤器并切掉末端:

    rsum.filter <- function(x, n = 3L) filter(x, rep(1, n))[-c(1, length(x))]
    

    或者更快,写成两个累计和的差:

    rsum.cumsum <- function(x, n = 3L) tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1)
    

    两者都只使用基本函数。一些基准:

    x <- sample(1:1000)
    
    rsum.rollapply <- function(x, n = 3L) rollapply(x, n, sum)
    rsum.sapply    <- function(x, n = 3L) sapply(1:(length(x)-n+1),function(i){
                                           sum(x[i:(i+n-1)])})
    
    library(microbenchmark)
    microbenchmark(
      rsum.rollapply(x),
      rsum.sapply(x),
      rsum.filter(x),
      rsum.cumsum(x)
    )
    
    # Unit: microseconds
    #               expr       min        lq    median         uq       max neval
    #  rsum.rollapply(x) 12891.315 13267.103 14635.002 17081.5860 28059.998   100
    #     rsum.sapply(x)  4287.533  4433.180  4547.126  5148.0205 12967.866   100
    #     rsum.filter(x)   170.165   208.661   269.648   290.2465   427.250   100
    #     rsum.cumsum(x)    97.539   130.289   142.889   159.3055   449.237   100
    

    另外我想如果x 并且所有应用的权重都是整数而不是数字,所有方法都会更快。

    【讨论】:

      【解决方案4】:

      只使用基础 R 你可以做到:

      v <- c(1, 2, 3, 10, 20, 30)
      grp <- 3
      
      res <- sapply(1:(length(v)-grp+1),function(x){sum(v[x:(x+grp-1)])})
      
      > res
      [1]  6 15 33 60
      

      另一种比 sapply 更快的方法(与 @flodel 的 rsum.cumsum 相比)如下:

      res <- rowSums(outer(1:(length(v)-grp+1),1:grp,FUN=function(i,j){v[(j - 1) + i]}))
      

      Flodel 的基准更新如下:

      x <- sample(1:1000)
      
      rsum.rollapply <- function(x, n = 3L) rollapply(x, n, sum)
      rsum.sapply    <- function(x, n = 3L) sapply(1:(length(x)-n+1),function(i){sum(x[i:(i+n-1)])})
      rsum.filter <- function(x, n = 3L) filter(x, rep(1, n))[-c(1, length(x))]
      rsum.cumsum <- function(x, n = 3L) tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1)
      rsum.outer <- function(x, n = 3L) rowSums(outer(1:(length(x)-n+1),1:n,FUN=function(i,j){x[(j - 1) + i]}))
      
      
      library(microbenchmark)
      microbenchmark(
        rsum.rollapply(x),
        rsum.sapply(x),
        rsum.filter(x),
        rsum.cumsum(x),
        rsum.outer(x)
      )
      
      
      # Unit: microseconds
      #              expr      min        lq     median         uq       max neval
      # rsum.rollapply(x) 9464.495 9929.4480 10223.2040 10752.7960 11808.779   100
      #    rsum.sapply(x) 3013.394 3251.1510  3466.9875  4031.6195  7029.333   100
      #    rsum.filter(x)  161.278  178.7185   229.7575   242.2375   359.676   100
      #    rsum.cumsum(x)   65.280   70.0800    88.1600    95.1995   181.758   100
      #     rsum.outer(x)   66.880   73.7600    82.8795    87.0400   131.519   100
      

      【讨论】:

      • 太棒了!谢谢。不幸的是,我无法投票,因为我没有足够的积分。
      • @user2834313: 没问题 ;)
      • 添加了一种新的可能方式 ;)
      【解决方案5】:

      如果你需要真正的速度,试试

      rsum.cumdiff <- function(x, n = 3L) (cs <- cumsum(x))[-(1:(n-1))] - c(0,cs[1:(length(x)-n)])
      

      一切都在 base R 中,更新 florel 的微基准测试不言而喻

      x <- sample(1:1000)
      
      rsum.rollapply <- function(x, n = 3L) rollapply(x, n, sum)
      rsum.sapply    <- function(x, n = 3L) sapply(1:(length(x)-n+1),function(i){sum(x[i:(i+n-1)])})
      rsum.filter <- function(x, n = 3L) filter(x, rep(1, n))[-c(1, length(x))]
      rsum.cumsum <- function(x, n = 3L) tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1)
      rsum.outer <- function(x, n = 3L) rowSums(outer(1:(length(x)-n+1),1:n,FUN=function(i,j){x[(j - 1) + i]}))
      rsum.cumdiff <- function(x, n = 3L) (cs <- cumsum(x))[-(1:(n-1))] - c(0, cs[1:(length(x)-n)])
      
      all.equal(rsum.rollapply(x), rsum.sapply(x))
      # [1] TRUE
      all.equal(rsum.sapply(x), rsum.filter(x))
      # [1] TRUE
      all.equal(rsum.filter(x), rsum.outer(x))
      # [1] TRUE
      all.equal(rsum.outer(x), rsum.cumsum(x))
      # [1] TRUE
      all.equal(rsum.cumsum(x), rsum.cumdiff(x))
      # [1] TRUE
      
      library(microbenchmark)
      microbenchmark(
        rsum.rollapply(x),
        rsum.sapply(x),
        rsum.filter(x),
        rsum.cumsum(x),
        rsum.outer(x),
        rsum.cumdiff(x)
      )
      
      # Unit: microseconds
      #               expr      min        lq       mean    median        uq       max neval
      #  rsum.rollapply(x) 3369.211 4104.2415 4630.89799 4391.7560 4767.2710 12002.904   100
      #     rsum.sapply(x)  850.425  999.2730 1355.56383 1086.0610 1246.5450  6915.877   100
      #     rsum.filter(x)   48.970   67.1525   97.28568   96.2430  113.6975   248.728   100
      #     rsum.cumsum(x)   47.515   62.7885   89.12085   82.1825  106.6675   230.303   100
      #      rsum.outer(x)   69.819   85.3340  160.30133   92.6070  109.0920  5740.119   100
      #    rsum.cumdiff(x)    9.698   12.6070   70.01785   14.3040   17.4555  5346.423   100
      
      ## R version 3.5.1 "Feather Spray"
      ## zoo and microbenchmark compiled under R 3.5.3
      

      奇怪的是,第二次通过微基准测试一切都更快了:

      microbenchmark(
             rsum.rollapply(x),
             rsum.sapply(x),
             rsum.filter(x),
             rsum.cumsum(x),
             rsum.outer(x),
             rsum.cumdiff(x)
         )
      
      # Unit: microseconds
      #               expr      min        lq       mean    median        uq      max neval
      #  rsum.rollapply(x) 3127.272 3477.5750 3869.38566 3593.4540 3858.9080 7836.603   100
      #     rsum.sapply(x)  844.122  914.4245 1059.89841  965.3335 1032.2425 5184.968   100
      #     rsum.filter(x)   47.031   60.8490   80.53420   74.1830   90.9100  260.365   100
      #     rsum.cumsum(x)   45.092   55.2740   69.90630   64.4855   81.4555  122.668   100
      #      rsum.outer(x)   68.850   76.6070   88.49533   82.1825   91.8800  166.304   100
      #    rsum.cumdiff(x)    9.213   11.1520   13.18387   12.1225   13.5770   49.456   100
      
      

      【讨论】:

        【解决方案6】:

        runner也可以使用

        x <- c(1, 2, 3, 10, 20, 30)
        
        runner::sum_run(x, k=3, na_pad = T)
        #> [1] NA NA  6 15 33 60
        

        或者slider也有用

        x <- c(1, 2, 3, 10, 20, 30)
        
        slider::slide_sum(x, before = 2, complete = T)
        #> [1] NA NA  6 15 33 60
        

        reprex package (v2.0.0) 于 2021-06-14 创建

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-07
          • 1970-01-01
          • 2019-10-02
          • 1970-01-01
          相关资源
          最近更新 更多