在R中快速方式做两个嵌套for循环

Ale*_*son 4 optimization for-loop r

我需要区分两个向量的任意两个元素.如果A<-c(1,2)B<-c(3,4)那么我的结果R应该是c(3-1,3-2,4-1,4-2).

有了这个片段

myfunction <- function(N)
{
  A = runif(N)
  B = runif(N)
  R = c()
  for(a in A){
    for(b in B){
      R=c(b-a,R)
    }
  }
  R
}
print(system.time(result <- myfunction(300)))
Run Code Online (Sandbox Code Playgroud)

我得到这个时间

   user  system elapsed 
  14.27    0.01   14.39 
Run Code Online (Sandbox Code Playgroud)

有没有更快的方法呢?

Jor*_*eys 5

最快的基础解决方案是使用outer:

as.vector(outer(B,A,"-"))
Run Code Online (Sandbox Code Playgroud)

令我惊讶的map2_dbl是,实际上比outer以下快一点:

不出我的意料,map2_dbl似乎更快,但那是因为它没有计算A和B中每个值的组合:

      test elapsed relative
3 CP(A, B)    7.54   47.125 # using expand.grid
2 JL(A, B)    0.16    1.000 # using map2_dbl
1 JM(A, B)    3.13   19.563 # using outer
Run Code Online (Sandbox Code Playgroud)

但:

> A <- 1:3
> B <- 3:1
> JL(A,B)
[1] -2  0  2
> JM(A,B)
[1]  2  1  0  1  0 -1  0 -1 -2
Run Code Online (Sandbox Code Playgroud)

这是两个长度为1000的向量,并且有100个重复.我没有包含你自己的解决方案,因为有两个原因,这个解决方案非常缓慢:

  • forR中的循环比过去快得多,但仍然不如使用循环编码C或等效的函数那样最优.这是测试代码中使用的函数的情况.
  • 你"成长"你的结果对象.每个循环通过代码,R变成一个更大的值,所以R必须在内存中寻找一个新的位置来存储它.这实际上是代码中最大的瓶颈.尽量避免使用这种构造,因为它是代码速度极慢的最重要原因之一.

基准代码:

library(tidyverse)

JM <- function(A,B){
  as.vector(outer(B,A,"-"))
}

JL <- function(A,B){
  map2_dbl(.x = A, 
           .y = B, 
           .f = ~ c(.x - .y))
}

CP <- function(A,B){
  as.data.frame(expand.grid(A,B)) %>%
    mutate(Var3 = Var2-Var1)
}

library(rbenchmark)

A <- runif(1000)
B <- runif(1000)

benchmark(JM(A,B),
          JL(A,B),
          CP(A,B),
          replications = 100,
          columns = c("test","elapsed","relative"))
Run Code Online (Sandbox Code Playgroud)