【问题标题】:Fastest way to "vectorize" a function with differing argument lengths“矢量化”具有不同参数长度的函数的最快方法
【发布时间】:2021-10-13 03:36:39
【问题描述】:

取如下简单函数:

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))
}

现在,如果我想映射所有输入值组合的查找表,我可以执行以下操作,例如,使用 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 越来越大,事情会变慢。

【问题讨论】:

  • pmap_df(d, function(a, b, c, d, e) fun(a = a, b = b, c = c, d = d, e = e)) 通常写成pmap_df(df, fun)。同样,the return function call is unnecessary.
  • 完全同意第一部分。我在脚本中粘贴了一些可以的情况。然而,第二部分非常主观(尽管您提出了一个很好的案例)。仅使用 return 也有很多好的理由(例如,便于非 r-native 共同作者的可读性)。
  • 我会告诫不要为非母语“演讲者”“简化”代码。学习语言是无可替代的。当然,代码不应该是“聪明的”,它应该是自我记录和清晰的。但它也应该是惯用的,并发挥其所用语言的优势。

标签: r function performance vectorization


【解决方案1】:

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

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

【讨论】:

  • 你能否补充一点解释为什么这比pmap_df(df, fun) 快​​得多?我猜“某事,某事,R 的内部复印店”?仍然有兴趣了解其他方法。
  • @RobG。对不起,我之前没有使用pmap_df。但我认为你的 fun 已经矢量化了。您需要做的就是将所有参数推送到函数中。
  • @Adam 是的,我使用 df 而不是 d 以避免命名冲突。
  • @Adam 改变了它。我想我使用 pmap 代替 bc 在我的用例中的某些部分没有矢量化。但这无疑帮助我更清楚地思考/向我展示了在哪里进一步矢量化!
  • @akrun 感谢您的赞赏 :)
【解决方案2】:

我认为与此等价的最直接的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

【讨论】:

    猜你喜欢
    • 2016-02-08
    • 2016-06-24
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多