2012-04-05 9 views
12

Aprovecho mucho el 'do.call' para generar llamadas a funciones. Por ejemplo,do.call en combinación con "::"

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

Sin embargo, a veces me gustaría llamar a una función explícitamente de un determinado paquete. Similar a, p. stats::rnorm(n=10, mean=5). ¿Hay alguna manera de que pueda utilizar do.call, o crear una función que se comporta igual que do.call para conseguir que esto funcione:

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

cómo sobre 'do.call (stats :: rnorm, myargs)'? – kohske

+1

@kohske - entonces parece 'stats :: rnorm (n = 10, mean = 5)' es aún más simple :-) – Tommy

+1

pero en ese caso no puedes usar list como su argumento :-( – kohske

Respuesta

18

No hay una función llamada "Estadísticas :: rnorm". Debe encontrar la función rnorm en el espacio de nombres "estadísticas":

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

Ahora se puede también, por supuesto pasar de un nombre como "estadísticas :: rnorm" y dividirlo en la parte del espacio de nombres y el nombre de la función:

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

actualización sólo quería demostrar que este enfoque es 2,5 veces más rápido que el de @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

Puede eliminar las comillas: esa será la función en sí, en lugar de su nombre.

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

Gracias por las respuestas. Creo que voy a algo como esto:

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

Eso parece ser una buena generalización de do.call por lo que todavía puede pasar en una cadena de caracteres para el argumento what, sin embargo, perfectamente emula una llamada 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 que es bueno de esto es que si la función que estoy llamando usos match.call() para almacenar una llamada en algún lugar, se conservará el nombre de la función real. Por ejemplo:

do.call.jeroen ("Información :: GLM", lista (fórmula = velocidad ~ Dist, data = as.name ('coches')))

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

Realmente no amo el hecho de que recurras a 'deparse',' parse' y 'eval' para resolver esto. Para empezar, no maneja' f <-function (x) do.call.jeroen (x, list (n = 10))); f (runif) ', y es más de dos veces más lento que llamar a' do.call' ... Actualicé mi respuesta con una alternativa. – Tommy

+0

Gracias. También actualicé mi respuesta un poco. Una cosa que me gusta sobre 'do.call.jeroen' es que el nombre original de la función permanece preservado, en lugar de reemplazarlo con una función anónima. – Jeroen

Cuestiones relacionadas