问题

我在R中有一个大的性能问题.我写了一个函数,它遍历一个data.frame对象.它只是添加一个新的col到data.frame和累积sth. (简单操作). data.frame有大约850.000行.我的电脑现在仍然工作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 ,我不需要混淆名称. 那么它是多么好?

我使用 data.frame 来运行 data.frame 的每个函数,从1000到10000乘以1000,并用 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秒.

Power of vectorization

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

  • conditioning
  • initialization of the results (which are 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.

性能

Tuning the tuned

另一个调整是在循环索引中改变 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行低于一秒.

性能

我想你可以进一步,我看到至少两件事情可以做:

  • write a C code to do conditional cumsum
  • if you know that in your data max sequence isn't large then you can change loop to vectorized while, something like

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

用于模拟和数字的代码为在GitHub 上可用. / p>




相关问题推荐