【问题标题】:How to optimize a code by avoiding loops?如何通过避免循环来优化代码?
【发布时间】:2019-11-17 19:52:27
【问题描述】:

我遇到了一些需要花费大量时间运行的代码的问题。有人可以帮我吗?提前致谢!

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")=List of 2 ..$ : NULL ..$ : chr [1:30007] "V1" "V2" "V3" "V4" ...

【问题讨论】:

标签: r optimization


【解决方案1】:

For-loops 和使用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))

【讨论】:

    【解决方案2】:
    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 循环,并创建一个应用循环而不是 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的nice解决方案的使用时间:

    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
    

    【讨论】:

      猜你喜欢
      • 2023-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-20
      • 1970-01-01
      • 2017-06-04
      • 2020-06-15
      • 2018-12-27
      相关资源
      最近更新 更多