2011-12-05 17 views
5

Me gustaría escribir un contenedor alrededor de una función personalizada que tome algunos vectores como entrada (como: mtcars$hp, mtcars$am etc.) para tomar la entrada como nombre de marco de datos (como data parámetro, ej .: mtcars) y nombres de variables (como: hp y am), como es habitual en la mayoría de las funciones estándar.`With` uso dentro de la función (contenedor)

Pero tengo algunos problemas, mi función propuesta 'demo' (una envoltura alrededor de mean no funciona

Código:.

f <- function(x, data=NULL) { 
    if (!missing(data)) { 
     with(data, mean(x)) 
    } else { 
     mean(x) 
    } 
} 

Correr contra un vector trabajos de curso:

> f(mtcars$hp) 
[1] 146.69 

Pero with falla desafortunadamente:

> f(hp, mtcars) 
Error in with(d, mean(x)) : object 'hp' not found 

Mientras que en el entorno global/sin mi función personalizada funciona nada:

> with(mtcars, mean(hp)) 
[1] 146.69 

me han tratado de hacer algún experimento con substitute, deparse y otros, pero sin ningún éxito. Cualquier sugerencia sería bienvenida!

+5

Eche un vistazo al artículo de wiki de @Hadley Wickham aquí: https://github.com/hadley/devtools/wiki/Evaluation – Andrie

+1

¿No debería ser 'f (hp, mtcars)'? – James

+1

Otra opción que quizás desee explorar es usar una fórmula, por lo que lo llamaría de forma ligeramente diferente - foo (~ hp, mtcars) - y luego usar cosas como model.frame para obtener los valores. – Spacedman

Respuesta

10

Aquí es la pieza clave del rompecabezas:

f <- function(x,data=NULL) { 
    eval(match.call()$x,data) # this is mtcars$hp, so just take the mean of it or whatever 
} 

> f(hp,mtcars) 
[1] 110 110 93 110 175 105 245 62 95 123 123 180 180 180 205 215 230 66 52 65 97 150 150 245 175 66 
[27] 91 113 264 175 335 109 

# it even works without a data.frame specified: 
> f(seq(10)) 
[1] 1 2 3 4 5 6 7 8 9 10 

Ver @ enlace de Andrie a @ documento de Hadley para una explicación de por qué funciona. Consulte la nota de @ Hadley para una advertencia crítica: f() no se puede ejecutar desde dentro de otra función.

Básicamente R usa la evaluación diferida (por ejemplo, no evalúa las cosas hasta que realmente se utilizan). Así que puede salirse con la suya pasándolo hp porque sigue siendo un símbolo no evaluado hasta que aparece en algún lugar. Como match.call lo toma como un símbolo y espera para evaluarlo, todo está bien.

Luego eval lo evalúa en el entorno especificado. De acuerdo con ?eval, el segundo argumento representa:

Entorno en el que se evaluará expr. También puede ser NULL, una lista de , un marco de datos, una pairlist o un entero como se especifica en sys.call.

Por lo tanto, está en buena forma con NULL (si no está pasando un data.frame) o un data.frame.

Prueba de evaluación perezosa es que este no devuelve un error (puesto que x nunca se utiliza en la función):

> g <- function(x) { 
+ 0 
+ } 
> g(hp) 
[1] 0 
+0

¡Qué idioma elegante! Gracias a ti (y Hadley) por eso. –

+0

Mayormente Hadley. Lo recordé pero no lo suficiente como para sacarlo de mi sombrero sin hacer referencia a su (impresionante) wiki. –

+0

Muy ordenado de hecho. Con todo, TODOS los problemas son RT (F) M relacionados ... – aL3xa

-1

probar esto:

f <- function(x, data = NULL) { 
    if (is.null(data)) { 
     mean(x) 
    } else { 
     attach(data) 
     mean(x) 
     detach(data) 
    } 
} 

también en su ejemplo se introduce el conjunto de datos en lugar de la columna. Su ejemplo debería ser f (CV, mtcars)

+0

Buen esfuerzo, pero falta un paréntesis y no estoy seguro de que prefiera 'adjuntar' a' con', y 'f (hp, mtcars)' todavía falla con "hp no encontrado". –

3
f <- function(x, data=NULL) { 
    if (!missing(data)) { colname=deparse(substitute(x)) 
     mean(data[[colname]]) 
    } else { 
     mean(x) 
    } 
} 

f(hp, mtcars) 
[1] 146.6875 

(Es cierto que no es tan compacto como sea @ GSK y creo que Trataré de recordar su método sobre el mío. Y gracias a Josh O'Brien por señalar un error que ahora se ha solucionado.)

+0

¡Gracias @DWin por este truco! – daroczig

Cuestiones relacionadas