基于条件但在条件结束后重置的累积和

Cumulative sum based on a condition but reset after condition ends

如何提高按条件重置累计和的性能?

我有一个 data.frame,在变量名称 demo 下方附加了一个演示数据集。我有一个 flag 列表示系统失败,然后是 continuousfailure 列,它计算故障连续发生的次数并在故障恢复后重置。我正在使用 tidyverse 包和 base R.

我一直在阅读一些关于这个问题的 Whosebug 帖子,但我似乎无法想出一个使用 tidyverse and/or base R 更快的解决方案。我已经实现了一个版本这使用 for 循环,但计算时间对于较大的数据集来说太长了(107388 行数据帧需要 9 分钟)。这个问题有更高效的解决方案吗?

测试数据集:

demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000), flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23), rep(FALSE, 13), rep(TRUE, 5), rep(FALSE, 19)),
continuousfailure = c(rep(0, 5), 1:10, rep(0, 25), 1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)

我目前使用的代码是:

for(i in 1:length(demo$data)) {
  if(demo$flag[i]) {
    bin <- 0
    for(j in 1:demo$continuousfailure[i]) {
      bin <- bin + demo$data[i - j + 1]
    }
    demo$magnitude[i] <- bin
  }
}

预期的输出应该是相同的,但是如果使用 tidyverse 或 base R 可以提高函数的速度,我们将不胜感激,同时还要解释一下逻辑是如何构建的。

谢谢!

我们可以使用data.tablerleid创建群组,returncumsumNA基于flag

library(dplyr)

demo %>%
  group_by(group = data.table::rleid(flag)) %>%
  mutate(new_mag = if(first(flag)) cumsum(data) else NA) %>%
  ungroup %>%
  select(-group)

#     data flag  continuousfailure magnitude new_mag
#    <dbl> <lgl>             <dbl>     <dbl>   <dbl>
# 1 -1121. FALSE                 0       NA      NA 
# 2  -460. FALSE                 0       NA      NA 
# 3  3117. FALSE                 0       NA      NA 
# 4   141. FALSE                 0       NA      NA 
# 5   259. FALSE                 0       NA      NA 
# 6  3430. TRUE                  1     3430.   3430.
# 7   922. TRUE                  2     4352.   4352.
# 8 -2530. TRUE                  3     1822.   1822.
# 9 -1374. TRUE                  4      448.    448.
#10  -891. TRUE                  5     -443.   -443.
# … with 90 more rows

其中 magnitude 是具有来自 for 循环的值的列,new_mag 是上述代码的输出。


创建群组的方法有多种。一种是如上所示使用 rleid,另一种是使用 dplyrcumsum

中的 lag
group_by(group = cumsum(flag != lag(flag, default = first(flag)))) %>%

还有一个 base rle

group_by(group = with(rle(flag), rep(seq_along(lengths), lengths)))

您可以将 group_by 行替换为上述任何内容。

数据

set.seed(123)
demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000), 
flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23),rep(FALSE, 13),
rep(TRUE, 5), rep(FALSE, 19)),continuousfailure = c(rep(0, 5), 1:10, rep(0, 25), 
1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)

我们可以使用data.table方法

library(data.table)
setDT(demo)[,   new := if(first(flag)) cumsum(data) else NA_real_, rleid(flag)]

数据

set.seed(123)
demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000), 
flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23),rep(FALSE, 13),
rep(TRUE, 5), rep(FALSE, 19)),continuousfailure = c(rep(0, 5), 1:10, rep(0, 25), 
1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)