对于确切的问题有很好的答案,但有一些关于一般 R 实践的注释。
在顺序无关紧要时使用
在 OP 中,我们使用 by = u 以便每一行一次运行一个。这是低效的! data.table 将为您排序,确定分组,并且由于它们是真正的非常随机的数字,因此最终分组与行一样多。
相反,我们可以使用Map() 或mapply() 来遍历行,这将提高性能。请注意,尚不清楚 a 和 b 是否真的因行而异 - 如果它们确实是常量,我们可能希望将它们从 data.table 中取出并作为常量传递。
uniroot2 = function(...) uniroot(...)$root ## helper function
dt[, c2 := mapply(uniroot2, u, a,b,
MoreArgs = list (f = froot,
interval = c(0.01, 10),
extendInt = 'yes'))]
## for n = 5000
## # A tibble: 2 x 13
## expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
## <bch:expr> <bch:t> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
## 1 OP 1.17s 1.17s 0.851 170KB 2.55 1 3 1.17s
## 2 no_by 857.2ms 857.2ms 1.17 214KB 3.50 1 3 857.2ms
##
## Warning message:
## Some expressions had a GC in every iteration; so filtering is disabled.
注意,一旦我们在mapply 中设置了它,使用future.apply::future_mapply() 来并行化我们的调用就很简单了。这比我笔记本电脑上的上述 no_by 示例快 2.5 倍。
library(future.apply)
plan(multisession)
dt[, c3 := future_mapply(uniroot2, u, a,b,
MoreArgs = list (f = froot,
interval = c(0.01, 10),
extendInt = 'yes')
, future.globals = "cumhaz")] ## see next section for how we could remove this
函数调用需要时间
在您的示例中,您将两个函数定义为:
cumhaz <- function(t, a, b) b * (t/b)^a
froot <- function(x, u, a, b) cumhaz(x, a, b) - u
当性能是一个问题并且简化很简单时,您可能想要简化。
froot2 = function(x, u, a, b) b * (x / b) ^ a - u
超过一百万个循环,对cumhaz() 的额外调用加起来:
x = 2.5; u = 1.5; a = 0.5; b = 1
bench::mark(froot_rep = for (i in 1:1e6) {froot(x=x, u=u, a=a, b=b)},
froot2_rep = for (i in 1:1e6) {froot2(x=x, u=u, a=a, b=b)})
## # A tibble: 2 x 13
## expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
## <bch:expr> <bch:t> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
## 1 froot_rep 4.74s 4.74s 0.211 13.8KB 3.38 1 16 4.74s
## 2 froot2_rep 3.17s 3.17s 0.315 13.8KB 2.84 1 9 3.17s
##
## Warning message:
## Some expressions had a GC in every iteration; so filtering is disabled.
因为uniroot 将进一步增加调用次数,默认最大迭代次数为 1,000!这意味着cumhaz() 在优化过程中会花费我们 1.5 到 1,500 秒之间的时间。作为@G。格洛腾迪克指出,有时我们实际上可以直接求解并使用直接向量化的方法,而不是依赖uniroot 或optimize。