2012-04-05 1 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

+1

@kohske - тогда это выглядит 'stats :: rnorm (n = 10, mean = 5)' еще проще :-) – Tommy

+1

, но в этом случае вы не можете использовать список в качестве аргумента :-( – kohske

ответ

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

Update Я просто хотел показать, что такой подход 2.5x быстрее, чем один из @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 <-функция (x) do.call.jeroen (x, list (n = 10)); f (runif) ', и это более чем в два раза медленнее, чем вызов' do.call' ... Я обновил свой ответ альтернативой. – Tommy

+0

Спасибо. Я также немного обновил свой ответ. о 'do.call.jeroen' заключается в том, что исходное имя функции остается сохраненным, вместо замены его анонимной функцией. – Jeroen

Смежные вопросы