2012-04-05 17 views
12

Uso molto di "do.call" per generare chiamate di funzione. E.g:do.call in combinazione con "::"

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

Tuttavia, a volte vorrei richiamare esplicitamente una funzione da un determinato pacchetto. Simile ad es. stats::rnorm(n=10, mean=5). C'è un modo posso usare do.call, o creare una funzione che si comporta esattamente come do.call per ottenere questo al lavoro:

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

come su 'do.call (stats :: rnorm, myargs)'? – kohske

+1

@kohske - quindi sembra 'stats :: rnorm (n = 10, mean = 5)' è ancora più semplice :-) – Tommy

+1

ma in questo caso non è possibile utilizzare la lista come argomento :-( – kohske

risposta

18

Non esiste una funzione chiamata "Statistiche :: RNorm". È necessario trovare la funzione rnorm nello spazio dei nomi "statistiche":

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

Ora si può ovviamente anche andare da un nome come "statistiche :: RNorm" e dividerlo in parte dello spazio dei nomi e il nome della funzione:

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

Aggiornamento volevo solo dimostrare che questo approccio è 2,5 volte più veloce di quello da @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

Puoi rimuovere le virgolette: questa sarà la funzione stessa, piuttosto che il suo nome.

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

Grazie per le risposte. Credo che sto andando per qualcosa di simile:

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, ...); 
} 

che sembra essere una bella generalizzazione della do.call modo che io possa ancora passare su una stringa di caratteri per l'argomento what, eppure emula ordinatamente una chiamata 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); 

Una cosa che è bella di questo è che se la funzione che io chiamo usi match.call() per memorizzare la chiamata da qualche parte, si manterrà il nome della funzione reale. Ad esempio:

do.call.jeroen ("statistiche :: glm", la lista (formula = velocità ~ dist, dati = as.name ('auto')))

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

Non amo molto il fatto che tu faccia ricorso a 'deparse',' parse' e ​​'eval' per risolvere questo problema.Per uno, non gestisce' f <-function (x) do.call.jeroen (x, list (n = 10)); f (runif) ', ed è più che due volte lento come chiamare' do.call' ... Ho aggiornato la mia risposta con un'alternativa – Tommy

+0

Grazie, ho anche aggiornato un po 'la mia risposta. Una cosa che mi piace su 'do.call.jeroen' è che il nome della funzione originale rimane conservato, invece di sostituirlo con una funzione anonima. – Jeroen

Problemi correlati