4

我不确定我是否使用do.call了正确的方法:

test <- function(test) {
  string <- deparse(substitute(test))
  start <- regexpr("\\(", string)
  end <- regexpr(")", string) - 1
  distribution <- substr(string, 0, start-1)
  string.arguments <- substr(string, start+1, end)
  v <- read.table(text=unlist(strsplit(string.arguments, ",")))
  list.arguments <- lapply(t(v), function(x) x)

  for (i in 1:1000000) {
    do.call(distribution, list.arguments)
  } 
}

这里的目标是能够发送分布,例如rnormand rgamma,后跟参数给函数,而不是评估函数。

这是使用 do.call 和只是简单地调用函数的比较:

> system.time(test(rnorm(100, 1, 10))) 
   user  system elapsed 
   17.772   0.000  17.820 
> system.time(for(i in 1:1000000) { rnorm(100,0,1)} )
   user  system elapsed 
   13.940   0.004  14.015 

问题是双重的:

  • do.call 真的需要多花 20% 的时间吗?
  • 这是接受不同分布和参数的正确方法吗?
4

1 回答 1

10

do.call总是比直接调用函数要慢,因为它必须通过你的参数并在调用之前找到函数。它变慢的程度取决于它有多少额外的计算来分摊这种开销。

> system.time(for(i in 1:1e6) do.call(rnorm, list(100)))
   user  system elapsed 
  13.55    0.00   13.58 
> system.time(for(i in 1:1e6) rnorm(100))
   user  system elapsed 
  11.40    0.00   11.42 

然而:

> system.time(for(i in 1:1e2) do.call(rnorm, list(1e6)))
   user  system elapsed 
   9.14    0.00    9.15 
> system.time(for(i in 1:1e2) rnorm(1e6))
   user  system elapsed 
   9.14    0.00    9.14 

此外,您的某些减速是由于您的正则表达式和其他与do.call固有速度无关的字符串操作。虽然速度很快,因为它运行在非常小的输入上,但它仍然是不必要的复杂。为什么不这样做:

test <- function(distrib, ..., N=1e6)
lapply(seq(N), function(x) distrib(...))

test(rnorm, 100, 1, 10)

或这个:

test <- function(call, N=1e6)
{
    call <- substitute(call)
    lapply(seq(N), function(...) eval.parent(call))
}

test(rnorm(100, 1, 10))
于 2013-07-13T11:04:20.390 回答