如何通过避免循环来优化代码?

时间:2019-07-08 10:01:07

标签: r optimization

我在编写代码时遇到了一些麻烦,这需要花费大量时间才能运行。有人可以给我些帮助吗?预先感谢!

all_dist=c()
ddim=dim(b)[1]
ddimi=ddim-1

for (k in 1:dim(b)[2]){
    for (i in seq(1,ddimi,1)){
        for (j in seq(i+1,ddim,1)){
        ze=(b[i,k])-(b[j,k])*(b[i,k])-(b[j,k])
        all_dist=c(all_dist,ze)
}}}

请注意:

str(b)
  

num [1:5,1:30007] -0.000292 -0.001384 -0.001412 -0.002603 -0.002848   ...    -attr(*,“ dimnames”)=列表2 .. $:NULL .. $:chr [1:30007]“ V1”“ V2”“ V3”“ V4” ...

2 个答案:

答案 0 :(得分:1)

set.seed(0)
b <- matrix(rnorm(5*30007), nrow=5)

all_dist=c()
ddim=dim(b)[1]
ddimi=ddim-1

system.time(
#With foor-Loop
for (k in 1:dim(b)[2]){
    for (i in seq(1,ddimi,1)){
        for (j in seq(i+1,ddim,1)){
        ze=(b[i,k])-(b[j,k])*(b[i,k])-(b[j,k])
        all_dist=c(all_dist,ze)
        }}}
)
#       User      System verstrichen 
#    104.568       3.636     108.206 


#Vectorized with matrix indices
system.time({
K <- 1:dim(b)[2]     #for (k in 1:dim(b)[2]){... creates this vector
I <- seq(1,ddimi,1)  #for (i in seq(1,ddimi,1)){... creates this vector
J <- unlist(lapply(I+1, function(x) seq(x,ddim,1)))  #for (j in seq(i+1,ddim,1)){... creates this vector

IK <- as.matrix(expand.grid(I, K))  #Get all combinations between I and K as you will have with the nested for loops of k and i
IK <- IK[rep(seq_len(nrow(IK)), rep((ddim-1):1,length.out=nrow(IK))),]  #IK-rows need to be repeated, as it is used repeatedly in the "for (j in seq(i+1,ddim,1)){" loop
JK <- as.matrix(expand.grid(j=J, k=K)) #Get all combinations between J and K as you will have with the nested for loops of k and j

#Now you have all the indexes of your for loop as vectors and can make the calculations
tt <- b[IK] - b[JK] * b[IK] - b[JK]
})
#      User      System verstrichen 
#      0.056       0.000       0.097 


identical(all_dist, tt)
#[1] TRUE

当您仅在左侧使用k而不与其他循环交互时,只需离开k循环和索引就可以部分向量化。

system.time({
tt=c()
for (i in seq(1,ddimi,1)){
  for (j in seq(i+1,ddim,1)){
    tt=c(tt, (b[i,])-(b[j,])*(b[i,])-(b[j,]))
  }}
dim(tt)  <- c(30007, 10)
tt <- as.vector(t(tt))
})
#       User      System verstrichen 
#      0.017       0.000       0.017 
identical(all_dist, tt)
#[1] TRUE

或者您可以将内部两个for循环替换为索引向量,并进行Apply循环,而不是k-for循环:

system.time({
I <- seq(1,ddimi,1)
J <- unlist(lapply(I+1, function(x) seq(x,ddim,1)))
I <- I[rep(seq_along(I), rep((ddim-1):1,length.out=length(I)))]
tt  <- as.vector(apply(b, 2, function(x) {x[I] - x[J] * x[I] - x[J]}))
})
#       User      System verstrichen 
#      0.085       0.000       0.085 
identical(all_dist, tt)
#[1] TRUE

gersht中好的解决方案的使用时间:

system.time({
mat <- as.vector(sapply(as.data.frame(b), function(x) {y <- combn(x, 2); y[1,] - y[2,] * y[1,] - y[2,]}))
})
#       User      System verstrichen 
#      1.083       0.000       1.082 
identical(all_dist, mat)
#[1] TRUE

答案 1 :(得分:1)

For循环和使用c()来增加向量会使您放慢速度。最好尝试利用矢量化的优势,并尽可能多地使用* apply(或map)功能。通过使用sapply遍历各列,创建组合并计算这些组合的乘积和差异,可以完成以下两项工作:

mat <- sapply(b, function(x) {y <- combn(x, 2); y[1,] - y[2,] * y[1,] - y[2,]})

它应该很快-可能不如user10488504的非常有效的解决方案那么快,但仍然非常快。它还具有非常严格的语法,并且输出是矩阵,每个列对应于b中的一列,您可能还会发现它很有用。

数据:

set.seed(12345)
b <- as.data.frame(matrix(runif(5*30007, -.001, -.0003), byrow = T, nrow = 5))