performance - 加速R中的循环操作

标签 performance r loops rcpp r-faq

我在 R 中遇到了很大的性能问题。我编写了一个迭代 data.frame 对象的函数。它只是向 data.frame 添加一个新列并积累一些内容。 (操作简单)。 data.frame 大约有 850K 行。我的电脑仍在工作(现在大约 10 小时),我不知道运行时间。

dayloop2 <- function(temp){
    for (i in 1:nrow(temp)){    
        temp[i,10] <- i
        if (i > 1) {             
            if ((temp[i,6] == temp[i-1,6]) & (temp[i,3] == temp[i-1,3])) { 
                temp[i,10] <- temp[i,9] + temp[i-1,10]                    
            } else {
                temp[i,10] <- temp[i,9]                                    
            }
        } else {
            temp[i,10] <- temp[i,9]
        }
    }
    names(temp)[names(temp) == "V10"] <- "Kumm."
    return(temp)
}

有什么想法可以加快此操作吗?

最佳答案

最大的问题和无效的根源是索引 data.frame,我的意思是使用 temp[,] 的所有这些行。
尽量避免这种情况。我接受了你的函数,更改了索引,这里version_A

dayloop2_A <- function(temp){
    res <- numeric(nrow(temp))
    for (i in 1:nrow(temp)){    
        res[i] <- i
        if (i > 1) {             
            if ((temp[i,6] == temp[i-1,6]) & (temp[i,3] == temp[i-1,3])) { 
                res[i] <- temp[i,9] + res[i-1]                   
            } else {
                res[i] <- temp[i,9]                                    
            }
        } else {
            res[i] <- temp[i,9]
        }
    }
    temp$`Kumm.` <- res
    return(temp)
}

如您所见,我创建了收集结果的向量res。最后,我将其添加到 data.frame 中,并且不需要弄乱名称。 那么如何更好呢?

我使用 nrow 从 1,000 到 10,000 × 1,000 运行 data.frame 的每个函数,并使用 system.time 测量时间

X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))

结果是

performance

您可以看到您的版本与nrow(X)呈指数关系。修改后的版本具有线性关系,简单的lm模型预测850,000行的计算需要6分10秒。

矢量化的力量

正如 Shane 和 Calimo 在他们的回答中指出的那样,矢量化是提高性能的关键。 从您的代码中,您可以移出循环:

  • 调理
  • 结果初始化(temp[i,9])

这导致了这段代码

dayloop2_B <- function(temp){
    cond <- c(FALSE, (temp[-nrow(temp),6] == temp[-1,6]) & (temp[-nrow(temp),3] == temp[-1,3]))
    res <- temp[,9]
    for (i in 1:nrow(temp)) {
        if (cond[i]) res[i] <- temp[i,9] + res[i-1]
    }
    temp$`Kumm.` <- res
    return(temp)
}

比较此函数的结果,这次是 nrow 从 10,000 到 100,000,乘以 10,000。

performance

调整已调音

另一个调整是将循环索引 temp[i,9] 更改为 res[i] (这在第 i 次循环迭代中完全相同) 。 这又是索引向量和索引 data.frame 之间的区别。
第二件事:当您查看循环时,您可以看到不需要循环所有 i,而只需循环那些符合条件的。
那么我们开始吧

dayloop2_D <- function(temp){
    cond <- c(FALSE, (temp[-nrow(temp),6] == temp[-1,6]) & (temp[-nrow(temp),3] == temp[-1,3]))
    res <- temp[,9]
    for (i in (1:nrow(temp))[cond]) {
        res[i] <- res[i] + res[i-1]
    }
    temp$`Kumm.` <- res
    return(temp)
}

您获得的性能很大程度上取决于数据结构。精确 - 条件中 TRUE 值的百分比。 对于我的模拟数据,850,000 行的计算时间低于一秒。

performance

我希望你能走得更远,我认为至少有两件事可以做:

  • 编写 C 代码来执行条件 cumsum
  • 如果您知道数据中的最大序列并不大,那么您可以将循环更改为矢量化 while,例如

    while (any(cond)) {
        indx <- c(FALSE, cond[-1] & !cond[-n])
        res[indx] <- res[indx] + res[which(indx)-1]
        cond[indx] <- FALSE
    }
    
<小时/>

用于模拟和图形的代码是 available on GitHub .

关于performance - 加速R中的循环操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2908822/

相关文章:

Java - 实例化对象后卡住

c - 使用 C/Intel 汇编,测试 128 字节内存块是否包含全零的最快方法是什么?

arrays - 如何正确优化 MArray 函数以提高速度?

r - 按日期序列为每个 id 生成行

php - 多个 WordPress 循环

python - 如何将文本中不同行的不同数字相乘

MySQL 简单 COUNT 花费很长时间

javascript - 在 Javascript 中分析异步函数

r - 如何在ggplot2中手动添加误差线和n=样本大小?

r - 如何处理 MatchIt 中无法识别的替换公式(例如)?