2012-04-05 46 views
12

我大量使用'do.call'來生成函數調用。例如:do.call結合「::」

myfun <- "rnorm"; 
myargs <- list(n=10, mean=5); 
do.call(myfun, myargs); 

但是,有時我想從某個包顯式調用一個函數。類似於stats::rnorm(n=10, mean=5)。有什麼辦法,我可以使用do.call,或創建一個行爲就像do.call得到這個工作的函數:

myfun <- "stats::rnorm"; 
myargs <- list(n=10, mean=5); 
do.call(myfun, myargs); 
+1

如何關於'do.call(stats :: rnorm,myargs)'? – kohske 2012-04-05 04:26:51

+1

@kohske - 然後它似乎'stats :: rnorm(n = 10,mean = 5)'更簡單:-) – Tommy 2012-04-05 05:16:49

+1

但在這種情況下,你不能使用list作爲它的參數:-( – kohske 2012-04-05 05:18:17

回答

18

有沒有所謂的「統計:: RNORM」功能。您必須在「統計」命名空間中找到rnorm功能:

myfun <- get("rnorm", asNamespace("stats")) 
myargs <- list(n=10, mean=5); 
do.call(myfun, myargs); 

現在,你當然也可以從一個名字去,如「統計:: RNORM」,並將其分成命名空間部分和函數名稱:

funname <- "stats::rnorm" 
fn <- strsplit(funname, "::")[[1]] 
myfun <- if (length(fn)==1) fn[[1]] else get(fn[[2]], asNamespace(fn[[1]])) 
myargs <- list(n=10, mean=5); 
do.call(myfun, myargs); 

更新我只是想證明,該方法是2.5倍的速度比從@Jeroen一...

do.call.tommy <- function(what, args, ...) { 
    if(is.character(what)){ 
    fn <- strsplit(what, "::")[[1]] 
    what <- if(length(fn)==1) { 
     get(fn[[1]], envir=parent.frame(), mode="function") 
    } else { 
     get(fn[[2]], envir=asNamespace(fn[[1]]), mode="function") 
    } 
    } 

    do.call(what, as.list(args), ...) 
} 

# Test it 
do.call.tommy(runif, 10) 
f1 <- function(FUN) do.call.tommy(FUN, list(5)) 
f2 <- function() { myfun<-function(x) x; do.call.tommy(myfun, list(5)) } 
f1(runif) 
f1("stats::runif") 
f2() 

# Test the performance...  
system.time(for(i in 1:1e4) do.call.jeroen("stats::runif", list(n=1, max=50))) # 1.07 secs 
system.time(for(i in 1:1e4) do.call.tommy("stats::runif", list(n=1, max=50))) # 0.42 secs 
11

你可以刪除引號:這將是函數本身,而不是它的名字。

myfun <- stats::rnorm 
myargs <- list(n=10, mean=5) 
do.call(myfun, myargs) 
0

感謝您的回覆。我想我會這樣的事情:

do.call.jeroen <- function(what, args, ...){ 
    if(is.function(what)){ 
    what <- deparse(as.list(match.call())$what); 
    } 
    myfuncall <- parse(text=what)[[1]]; 
    mycall <- as.call(c(list(myfuncall), args)); 
    eval(mycall, ...); 
} 

這似乎是do.call一個很好的概括,這樣我仍然可以在一個字符串傳爲what說法,但它巧妙地模仿stats::rnorm(n=10, mean=5)電話。

myfun1 <- "rnorm"; 
myfun2 <- "stats::rnorm"; 
myargs <- list(n=10, mean=5); 
do.call.jeroen(myfun1, myargs); 
do.call.jeroen(myfun2, myargs); 
do.call.jeroen(rnorm, myargs); 
do.call.jeroen(stats::rnorm, myargs); 

一兩件事,是關於這個漂亮的是,如果我打電話用途match.call()來存儲調用某個函數,它將保留在實際的函數名。例如:

do.call.jeroen( 「統計:: GLM」,列表(式=速度〜DIST,數據= as.name( '汽車')))

Call: stats::glm(formula = speed ~ dist, data = cars) 

Coefficients: 
(Intercept)   dist 
    8.2839  0.1656 

Degrees of Freedom: 49 Total (i.e. Null); 48 Residual 
Null Deviance:  1370 
Residual Deviance: 478 AIC: 260.8 
+0

我真的不喜歡事實上,你訴諸'deparse','parse'和'eval'來解決這個問題。例如,它不處理'f <-function(x)do.call.jeroen(x,list(n = 10) )); f(runif)',它的速度是調用'do.call'的兩倍以上......我用一個替代方法更新了我的答案 – Tommy 2012-04-06 02:22:32

+0

謝謝,我也更新了一下我的回答。關於'do.call.jeroen'的原始功能名稱保持不變,而不是用匿名函數替換它。 – Jeroen 2012-04-06 07:44:40