避免在R中使用两个for循环

use*_*599 10 loops r

我有一个R代码,可以进行两个函数的卷积...

convolveSlow <- function(x, y) {  
nx <- length(x); ny <- length(y)  
xy <- numeric(nx + ny - 1)  
for(i in seq(length = nx)) {  
 xi <- x[[i]]  
        for(j in seq(length = ny)) {  
            ij <- i+j-1  
            xy[[ij]] <- xy[[ij]] + xi * y[[j]]  
        }  
    }  
    xy  
}  
Run Code Online (Sandbox Code Playgroud)

有没有办法删除两个for循环并使代码运行得更快?

谢谢你

And*_*rie 18

由于R在计算向量运算方面非常快,因此在编程性能时要记住的最重要的事情是尽可能多地进行矢量化操作.

这意味着要努力用向量操作替换循环.这是我的快速卷积解决方案(使用长度为1000的输入向量,速度提高50倍):

convolveFast <- function(x, y) {
    nx <- length(x)
    ny <- length(y)
    xy <- nx + ny - 1
    xy <- rep(0, xy)
    for(i in (1:nx)){
        j <- 1:ny
        ij <- i + j - 1
        xy[i+(1:ny)-1] <- xy[ij] + x[i] * y
    }
    xy
}
Run Code Online (Sandbox Code Playgroud)

你会注意到内环(对于j in ...)已经消失了.相反,我用矢量操作替换它.j现在被定义为向量(j < - 1:ny).另请注意,我引用整个向量y,而不是对其进行子集化(即y而不是y [j]).

j <- 1:ny
ij <- i + j - 1
xy[i+(1:ny)-1] <- xy[ij] + x[i] * y
Run Code Online (Sandbox Code Playgroud)

我写了一个小函数来测量性能:

measure.time <- function(fun1, fun2, ...){
    ptm <- proc.time()
    x1 <- fun1(...)
    time1 <- proc.time() - ptm

    ptm <- proc.time()
    x2 <- fun2(...)
    time2 <- proc.time() - ptm

    ident <- all(x1==x2)

    cat("Function 1\n")
    cat(time1)
    cat("\n\nFunction 2\n")
    cat(time2)
    if(ident) cat("\n\nFunctions return identical results")

}
Run Code Online (Sandbox Code Playgroud)

对于每个长度为1000的两个向量,我的性能提升了98%:

x <- runif(1000)
y <- runif(1000)
measure.time(convolveSlow, convolveFast, x, y)

Function 1
7.07 0 7.59 NA NA

Function 2
0.14 0 0.16 NA NA

Functions return identical results
Run Code Online (Sandbox Code Playgroud)

  • +1好的解决方案.如果你想为你的功能计时,你不必乱用`proc.time`,你可以轻松使用`?system.time` (5认同)
  • 在循环之前移动j的定义以进一步加速. (2认同)

Dir*_*tel 10

  1. 对于向量,您使用索引[],而不是[[]],因此使用xy[ij]

  2. 卷积不容易矢量化,但一个常见的技巧是切换到编译代码.的写作R附加手册使用卷积作为运行示例,并且示出了几种可供选择的; 我们在Rcpp文档中也经常使用它.