"vectorize" 具有不同参数长度的函数的最快方法

Fastest way to "vectorize" a function with differing argument lengths

采用以下简单函数:

fun <- function(a, b, c, d, e) {
   
   stopifnot("Input you provide must be equal length." = length(a) == length(b) && length(b) == length(c) && length(c) == length(d) && length(d) == length(e))

   result <- (a + b / c + d) / sqrt(e)
   
   result2 <- a/result

   return(data.frame(result = result, result2 = result2, a = a, b = b, c = c, d = d, e = e))
}

现在,如果我想映射所有输入值组合的查找 table,我可以执行以下操作,例如,使用 purrr 泛函:

library(purrr)

df <- expand.grid(a = 1:1000, b = c(1, 2, 3, 4, 5), c = 7, d = 3, e = 5)

out <- pmap_df(d, fun)

然而,即使对于一个较大向量和一个较小向量的相对简单的情况(在我的应用案例中,这将是最常见的情况),这也非常慢。

Unit: seconds                                                                             
      min       lq     mean   median       uq      max neval
 2.235245 2.235245 2.235245 2.235245 2.235245 2.235245     1

如何加快速度,尤其是对于上面描述的简单案例?当然,随着 df 越来越大,事情会变慢。

我不能说我的解决方案是最快的,但确实更快。您可以试试下面的代码

do.call(fun, df)

和基准测试

df <- expand.grid(a = 1:1000, b = c(1, 2, 3, 4, 5), c = 7, d = 3, e = 5)


f_Rob <- function() pmap_df(df, function(a, b, c, d, e) fun(a = a, b = b, c = c, d = d, e = e))
f_TIC <- function() do.call(fun, df)

microbenchmark(
  f_Rob(),
  f_TIC(),
  unit = "relative",
  check = "equivalent",
  times = 10
)

你会看到

Unit: relative
    expr      min       lq     mean   median       uq      max neval
 f_Rob() 1074.886 1049.034 441.6319 854.2739 620.4029 92.29739    10
 f_TIC()    1.000    1.000   1.0000   1.0000   1.0000  1.00000    10

我认为最直接的 tidyverse 等同于使用 rlang 中的 exec()

这并不比 do.call() 快,除了高级案例之外我没有看到明显的优势,但它就是。

library(rlang)

df <- expand.grid(a = 1:1000, b = c(1, 2, 3, 4, 5), c = 7, d = 3, e = 5)

f_TIC <- function() do.call(fun, df)
f_rlang <- function() exec(fun, !!!df)

microbenchmark::microbenchmark(
  f_rlang(),
  f_TIC(),
  unit = "relative",
  check = "equivalent",
  times = 100
)

大约慢 15%。

Unit: relative
      expr      min       lq     mean   median       uq      max neval
 f_rlang() 1.158271 1.149351 1.156371 1.145274 1.143179 1.229871   100
   f_TIC() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000   100