我有一个可以对两个函数进行卷积的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循环并使代码运行得更快?
谢谢 圣
答案 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)
对于向量,您使用[]
而不是[[]]
进行索引,因此请使用xy[ij]
等
卷积不容易矢量化,但一个常见的技巧是切换到已编译的代码。 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