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

时间:2011-02-04 04:14:58

标签: r loops

我有一个可以对两个函数进行卷积的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  
}  

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

谢谢 圣

6 个答案:

答案 0 :(得分: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
}

你会注意到内循环(对于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

我写了一个小函数来衡量绩效:

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")

}

对于两个长度为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

答案 1 :(得分:10)

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

  2. 卷积不容易矢量化,但一个常见的技巧是切换到已编译的代码。 Writing R Extensions 手册使用卷积作为运行示例,并显示了几种替代方案;我们在Rcpp文档中也经常使用它。

答案 2 :(得分:2)

正如Dirk所说,编译代码可以快得多。我必须为我的一个项目做这件事,并对加速感到惊讶:比Andrie的解决方案快40倍。

> a <- runif(10000)
> b <- runif(10000)
> system.time(convolveFast(a, b))
   user  system elapsed 
  7.814   0.001   7.818 
> system.time(convolveC(a, b))
   user  system elapsed 
  0.188   0.000   0.188 

在我决定使用C代码之前,我曾多次尝试在R中加速这一点(注意:它确实不是这样)。我的所有人都比Andrie的慢,并且是适当增加交叉产品的变种。一个基本版本只需三行即可完成。

convolveNotAsSlow <- function(x, y) {
  xyt <- x %*% t(y)
  ds <- row(xyt)+col(xyt)-1
  tapply(xyt, ds, sum)
}

这个版本只有一点帮助。

> a <- runif(1000)
> b <- runif(1000)
> system.time(convolveSlow(a, b))
   user  system elapsed 
  6.167   0.000   6.170 
> system.time(convolveNotAsSlow(a, b))
   user  system elapsed 
  5.800   0.018   5.820 

我最好的版本是:

convolveFaster <- function(x,y) {
  foo <- if (length(x)<length(y)) {y %*% t(x)} else { x %*% t(y) }
  foo.d <- dim(foo)
  bar <- matrix(0, sum(foo.d)-1, foo.d[2])
  bar.rc <- row(bar)-col(bar)
  bar[bar.rc>=0 & bar.rc<foo.d[1]]<-foo
  rowSums(bar)
}

这好一点,但仍然没有Andrie的那么快

> system.time(convolveFaster(a, b))
   user  system elapsed 
  0.280   0.038   0.319 

答案 3 :(得分:2)

通过仔细使用整数数学并用seq.int(0L,ny-1L)替换(1:ny)-1L,可以稍微优化convolveFast函数:

convolveFaster <- function(x, y) {
    nx <- length(x)
    ny <- length(y)
    xy <- nx + ny - 1L
    xy <- rep(0L, xy)
    for(i in seq_len(nx)){
        j <- seq_len(ny)
        ij <- i + j - 1L
        xy[i+seq.int(0L, ny-1L)] <- xy[ij] + x[i] * y
    }
    xy
}

答案 4 :(得分:1)

convolve(x, rev(y), type = "open")中的stats怎么样?

> x <- runif(1000)
> y <- runif(1000)
> system.time(a <- convolve(x, rev(y), type = "o"))
   user  system elapsed 
  0.032   0.000   0.032 
> system.time(b <- convolveSlow(x, y))
   user  system elapsed 
 11.417   0.060  11.443 
> identical(a,b)
[1] FALSE
> all.equal(a,b)
[1] TRUE

答案 5 :(得分:-1)

有人说apply()和sapply()函数比R中的for()循环更快。你可以将卷积转换为函数并从apply()中调用它。 但是,有证据表明相反 http://yusung.blogspot.com/2008/04/speed-issue-in-r-computing-apply-vs.html