【问题标题】:Cumulative sum that resets when 0 is encountered遇到 0 时重置的累积和
【发布时间】:2015-09-10 12:27:16
【问题描述】:

我想对一个字段进行累积求和,但每当遇到 0 时都会重置聚合值。

这是我想要的一个例子:

data.frame(campaign = letters[1:4] , 
       date=c("jan","feb","march","april"),
       b = c(1,0,1,1) ,
       whatiwant = c(1,0,1,2)
       )

 campaign  date b whatiwant
1        a   jan 1         1
2        b   feb 0         0
3        c march 1         1
4        d april 1         2

【问题讨论】:

标签: r


【解决方案1】:

另一个基地只是

with(df, ave(b, cumsum(b == 0), FUN = cumsum))
## [1] 1 0 1 2

这只会根据0 的出现将b 列划分为组,并计算每个组的b 的累积总和


使用最新data.table 版本(v 1.9.6+)的另一种解决方案

library(data.table) ## v 1.9.6+
setDT(df)[, whatiwant := cumsum(b), by = rleid(b == 0L)]
#    campaign  date b whatiwant
# 1:        a   jan 1         1
# 2:        b   feb 0         0
# 3:        c march 1         1
# 4:        d april 1         2

每个 cmets 的一些基准

set.seed(123)
x <- sample(0:1e3, 1e7, replace = TRUE)
system.time(res1 <- ave(x, cumsum(x == 0), FUN = cumsum))
# user  system elapsed 
# 1.54    0.24    1.81 
system.time(res2 <- Reduce(function(x, y) if (y == 0) 0 else x+y, x, accumulate=TRUE))
# user  system elapsed 
# 33.94    0.39   34.85 
library(data.table)
system.time(res3 <- data.table(x)[, whatiwant := cumsum(x), by = rleid(x == 0L)])
# user  system elapsed 
# 0.20    0.00    0.21 

identical(res1, as.integer(res2))
## [1] TRUE
identical(res1, res3$whatiwant)
## [1] TRUE

【讨论】:

  • 这很烦人,需要计算cumsum 两次。 :-/
  • 你可以试试with(rle(df1$b!=0), sequence(lengths)*rep(values, lengths))
  • @akrun 我得到了不同的结果。也许你是对的,我们是错的,不知道。
  • 我根据列值为 0、1 的假设对其进行了编码。您的示例不同,因此它不起作用。
  • @akrun 哦,所以也许可以取消删除您的答案并将其作为假设。我猜在这种情况下,您的解决方案应该非常有效。
【解决方案2】:

另一个迟来的想法:

ff = function(x)
{
    cs = cumsum(x)
    cs - cummax((x == 0) * cs)
}
ff(c(0, 1, 3, 0, 0, 5, 2))
#[1] 0 1 4 0 0 5 7

并进行比较:

library(data.table)
ffdt = function(x) 
    data.table(x)[, whatiwant := cumsum(x), by = rleid(x == 0L)]$whatiwant

x = as.numeric(x) ##because 'cumsum' causes integer overflow
identical(ff(x), ffdt(x))
#[1] TRUE
microbenchmark::microbenchmark(ff(x), ffdt(x), times = 25)
#Unit: milliseconds
#    expr      min       lq   median       uq      max neval
#   ff(x) 315.8010 362.1089 372.1273 386.3892 405.5218    25
# ffdt(x) 374.6315 407.2754 417.6675 447.8305 534.8153    25

【讨论】:

    【解决方案3】:

    您可以将 Reduce 函数与自定义函数一起使用,当遇到的新值为 0 时返回 0,否则将新值添加到累积值中:

    Reduce(function(x, y) if (y == 0) 0 else x+y, c(1, 0, 1, 1), accumulate=TRUE)
    # [1] 1 0 1 2
    

    【讨论】:

      【解决方案4】:

      hutilscpp::cumsum_reset 就是为此目的而设计的。第一个参数是一个逻辑向量,指示累积和何时应该继续。第二个参数是累积和本身的输入

      library(hutilscpp)
      b <- c(1, 0, 1, 1)
      cumsum_reset(as.logical(b), b)
      

      在我的机器上,与上面的data.table函数相比,这种cumsum_reset的使用快了大约3倍。

      【讨论】:

        猜你喜欢
        • 2018-10-27
        • 1970-01-01
        • 2022-06-10
        • 2021-08-19
        • 2021-03-26
        • 2020-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多