【问题标题】:Cumulative sum based on a condition but reset after condition ends基于条件的累积和但在条件结束后重置
【发布时间】:2020-01-26 06:40:08
【问题描述】:

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

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

我一直在阅读有关此问题的一些 StackOverflow 帖子,但我似乎无法理解使用 tidyverse 和/或 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 来提高函数的速度,我们将不胜感激,同时解释一下逻辑是如何构建的。

谢谢!

【问题讨论】:

    标签: r grouping cumulative-sum


    【解决方案1】:

    我们可以使用data.tablerleid创建群组,并基于flag返回cumsum或NA

    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,另一种是使用lag from dplyr and cumsum

    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)
    

    【讨论】:

    • 谢谢!这可以解决问题并且反应灵敏。我非常感谢使用 dplyr 的 data.table 无包解决方案。
    【解决方案2】:

    我们可以使用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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-24
      • 2021-07-20
      • 1970-01-01
      • 2019-10-16
      • 2018-05-02
      • 1970-01-01
      • 1970-01-01
      • 2018-08-09
      相关资源
      最近更新 更多