【问题标题】:Speed up/alternative to for-loop that refers to prior calculated row加速/替代指向先前计算行的 for 循环
【发布时间】:2021-07-07 23:53:17
【问题描述】:

我有一些代码需要构建一个列,该列查看前一行并求和,直到达到某个点,该点返回 0 并重新开始。

目前这是通过 for 循环完成的,但是我想用它来处理的数据集之一是 300 万行,并且使用这种方法需要几天时间来处理。为了可用于我的目的,它需要在几秒钟内处理完毕。

有没有办法加快或替换 for 循环,以便更快地处理? (lag()之类的不能用,因为它们使用的是预计算数据)

DD <- c("Yes", "No", "No", "Yes", "No", "No", "No", "No", "No", "No", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "No", "Yes", "Yes", "Yes", "Yes", "Yes", "No", "No", "No")

dataOut <- data.frame(DD)

alarm.upper.limit <- 7.26
alarm.lower.limit <- -7.26

s <- 0.11

dataOut$weight <- if_else(dataOut$DD=="Yes", 1-s, 0-s)

dataOut$cusum[1] <- dataOut$weight[1]

for (j in 2:nrow(dataOut)) {
  dataOut$cusum[j] <- if(dataOut$cusum[j-1]<alarm.lower.limit){dataOut$cusum[j]=0}
  else if(dataOut$cusum[j-1]>alarm.upper.limit){dataOut$cusum[j]=0}
  else{(dataOut$weight[j])+(dataOut$cusum[j-1])}
}


【问题讨论】:

    标签: r performance for-loop


    【解决方案1】:

    我们可以使用accumulate,它应该比for循环操作更快

    library(dplyr)
    library(purrr)
    dataOut %>%
       mutate(cusum2 = accumulate(weight, ~
         if(between(.x, alarm.lower.limit, alarm.upper.limit)) .x + .y else 0))
    

    或者在base R中使用Reduce

    dataOut$cusum3 <- with(dataOut, Reduce(function(x, y)
      if(x >= alarm.lower.limit & x <= alarm.upper.limit) x + y 
           else 0, weight, accumulate = TRUE))
    

    在稍微大一点的数据上

    dataOut1 <- dataOut[rep(seq_len(nrow(dataOut)), 1e3), ]
     
     
    system.time({
     dataOut1$cusum[1] <- dataOut1$weight[1]
    
     for (j in 2:nrow(dataOut1)) {
       dataOut1$cusum[j] <- if(dataOut1$cusum[j-1]<alarm.lower.limit){
              dataOut1$cusum[j]=0}
       else if(dataOut1$cusum[j-1]>alarm.upper.limit){
             dataOut1$cusum[j]=0}
       else{
             (dataOut1$weight[j])+(dataOut1$cusum[j-1])}
     }
     
     })
    #  user  system elapsed 
    #  0.902   0.758   1.617 
    
    system.time({
       dataOut1 <-  dataOut1 %>%
                  mutate(cusum2 = accumulate(weight, 
       ~ if(between(.x, alarm.lower.limit, alarm.upper.limit)) .x + .y else 0))
    
    })
    
    #user  system elapsed 
    #  0.110   0.005   0.116 
    

    【讨论】:

    • 非常感谢,我使用了accumulate 方法,它将超过 6 小时的处理时间(我们实际上没有时间让它完成)缩短到 20 秒。在较小的集合上运行测试显示与 for 循环相同的输出。魔术。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-15
    • 2012-11-07
    • 1970-01-01
    • 2019-06-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多