for-loop运行得太慢了

时间:2018-02-13 09:42:41

标签: r for-loop

我写了一段R代码,但由于我不是专家,我觉得我的代码不是最优的,而且处理时间太长...... 如何改进此代码?

这是代码:

for (k in 1 : length(df_3L)) {
    vec <- c()
    for(i in 1 : dim(df_3L[[k]])[1]) {
        for(j in 1 : dim(df_3L[[k]])[1] - 1) {
            if (df_3L[[k]][i] == df_3L[[k]][j + 1]) {
                vec <- c(vec, 1)
            } else {
                vec <- c(vec, 0)
            }
        }
    }
    assign(paste0("vec_3L", k), vec)
}

有关详细信息,for循环中的k是主题数(40); df_3L文件是500行的向量。

2 个答案:

答案 0 :(得分:3)

您的代码运行缓慢,因为您正在循环中生成向量。这是典型的反模式,例如this article by Andrew Barr,参见无数其他模式。

解决方案是(1)预分配矢量并在特定索引处分配值,或者(2)放弃循环并使用列表函数(例如lapply)创建矢量。在你的情况下,由于嵌套循环而变得更难(尽管不是不可能)。因此,变体(1)开箱即可轻松实现:

替换

vec <- c()

vec = vector("numeric", dim(df_3kL[[k]])[1] * (dim(df_3kL[[k]])[1] - 1))

替换整个内循环if
vec[i] = if (df_3kL[[k]][i] == df_3kL[[k]][j + 1]) 1 else 0

现在,这不是好的代码。通过矢量化来改进它是留给读者的练习。

此外,您可以通过一致使代码立即可读:不要混合样式(例如<- vs =赋值,seq vs : ...)中间代码。

答案 1 :(得分:1)

您的代码运行缓慢,因为您正在执行紧密的内部循环,而不是依赖于矢量化函数。

使用模拟数据计时原始实现表明它确实很慢:

df_3L <- replicate(10, matrix(runif(200), nrow = 200), simplify = FALSE)

system.time({
    for (k in 1 : length(df_3L)) {
        vec <- c()
        for(i in 1 : dim(df_3L[[k]])[1]) {
            for(j in 1 : dim(df_3L[[k]])[1] - 1) {
                if (df_3L[[k]][i] == df_3L[[k]][j + 1]) {
                    vec <- c(vec, 1)
                } else {
                    vec <- c(vec, 0)
                }
            }
        }
        assign(paste0("vec_3L", k), vec)
    }
})
 ##   user  system elapsed 
 ## 21.270   0.008  21.599

任何时候你在R中的if .. else循环中看到for,你就可以确定它会变慢。解决方案是认识到我们可以一次完成比较而不是一次比较。这使我们可以摆脱整个内部循环,并大大加快代码速度:

system.time({
    for (k in 1 : length(df_3L)) {
        vec <- c()
        for(i in 1 : dim(df_3L[[k]])[1]) {
            vec <- c(vec, as.numeric(df_3L[[k]][i] == df_3L[[k]]))
        }
        assign(paste0("vec_3L", k), vec)
    }
})

##  user  system elapsed 
## 0.114   0.000   0.114 

事实上,我们甚至不需要i循环,为方便起见,我们可以使用lapply代替k循环。最终的惯用和快速实现可能如下所示:

system.time({
    vecs <- lapply(df_3L, function(x) {
        x <- x[, 1]
        as.numeric(do.call(`==`, expand.grid(x, x)))
    })
})
##  user  system elapsed 
## 0.016   0.000   0.016