2011-01-14 7 views
13

Profundizando en los misterios de la evaluación R ... Esto está estrechamente relacionado con mi pregunta anterior (How to write an R function that evaluates an expression within a data-frame). Digamos que quiero escribir una función topfn que tome un marco de datos y una expresión que implique nombres de columna de ese marco de datos. Quiero pasar ambos argumentos a otra función fn que realmente evalúa la expresión dentro del "entorno" del marco de datos. Y quiero tanto fn y topfn para que funcione correctamente cuando se pasa una trama de datos y una expresiónR: pasando la expresión a una función interna

Mi primer intento, como se sugiere en la respuesta a la pregunta anterior, es definir:

fn <- function(dfr, expr) { 
    mf <- match.call() 
    eval(mf$expr, envir = dfr) 
} 

topfn y definir así:

topfn <- function(df, ex) { 
    mf <- match.call() 
    fn(df, mf$ex) 
} 

Ahora bien, si tengo una trama de datos

df <- data.frame(a = 1:5, b = 1:5) 

la función interna fn funciona bien:

> fn(df,a) 
[1] 1 2 3 4 5 

Pero el topfn no funciona:

> topfn(df,a) 
mf$ex 

Para solucionar este primera vez que se comprueba la clase de topfn(df,a),

> class(topfn(df,a)) 
[1] "call" 

Esto me da una idea para un hack feo para redefinir fn de la siguiente manera:

fn <- function(dfr, expr) { 
    mf <- match.call() 
    res <- eval(mf$expr, envir = dfr) 
    if(class(res) == 'call') 
    eval(expr, envir = dfr) else 
    res 
} 

Y ahora ambas funciones de trabajo:

> fn(df,a) 
[1] 1 2 3 4 5 
> topfn(df,a) 
[1] 1 2 3 4 5 

Como ya he dicho, esto se ve como un truco feo. ¿Hay una mejor manera (o más lenguaje estándar) para que funcionen? He consultado el documento de normas de evaluación no estándar estándar curiosamente nombrado de Lumley http://developer.r-project.org/nonstandard-eval.pdf, pero no fui particularmente esclarecido después de leerlo. También son útiles los indicadores del código fuente de funciones que puedo consultar para ver ejemplos.

+0

Hay una gran wiki sobre el tema de la evaluación R por Hadley Wickham aquí: https: //github.com/hadley/devtools/wiki/Evaluation –

+1

Y como sugiero, hay que distinguir cuidadosamente entre las funciones que se usan interactivamente y las funciones que son llamados desde otras funciones. Es muy difícil llamar a cualquier función que utilice sustitutos y otros trucos. En otras palabras, no existe una manera limpia para que tanto 'fn' como' topfn' trabajen con los mismos tipos de entradas, y esto es BUENO. – hadley

+1

@hadley Entiendo su punto: las funciones diseñadas para uso interactivo pueden usar sustitutos/otros trucos para que sea fácil escribirlos en la consola (es decir, sin comillas, etc.). Y las funciones que solo están diseñadas para ser llamadas por otras funciones no deben usar esos trucos porque pueden romperse de forma impredecible. En particular, la solución provista por @Richie al final de su respuesta, a pesar de que funciona en mi escenario particular anterior, puede romperse (especialmente 'fn') cuando se usa en otro escenario, creo que eso es lo que tú ' está diciendo, ¿verdad? –

Respuesta

14

Esto es más fácilmente evitados por las cuerdas que pasan en topfn en lugar de expresiones.

topfn <- function(df, ex_txt) 
{ 
    fn(df, ex_txt) 
} 

fn <- function(dfr, expr_txt) 
{   
    eval(parse(text = expr_txt), dfr) 
} 

df <- data.frame(a = 1:5, b = 1:5) 
fn(df, "a")        
fn(df, "2 * a + b") 
topfn(df, "a")    
topfn(df, "2 * a + b") 

EDIT:

Usted puede dejar que las expresiones de paso de usuario en, pero el uso de cadenas por debajo de su conveniencia.

Cambio topfn a

topfn <- function(df, ex) 
{ 
    ex_txt <- deparse(substitute(ex)) 
    fn(df, ex_txt) 
} 
topfn(df, a)    
topfn(df, 2 * a + b) 

otra edición:

Esto parece funcionar:

topfn <- function(df, ex) 
{ 
    eval(substitute(fn(df, ex))) 
} 

fn <- function(dfr, expr) 
{   
    eval(substitute(expr), dfr) 
} 
fn(df, a)        
fn(df, 2 * a + b) 
topfn(df, a)    
topfn(df, 2 * a + b) 
+0

@richie: Gracias, sí, pero estaba buscando una manera de evitar pasar cadenas. Quiero que las funciones funcionen como muchas otras funciones en R que toman como argumentos las expresiones no citadas. –

+0

Ver mi respuesta a la pregunta anterior para una tercera especificación (estilo reticular) de 'fn'. http://stackoverflow.com/questions/4682709/how-to-write-an-r-function-that-evaluates-an-expression-within-a-data-frame –

+0

@Richie Sí, lo vi, gracias, es en realidad es la forma más sencilla de hacer que funcione para la función interna, pero adaptarla al caso anidado anterior es igualmente problemático. –

1

Puede usar tres puntos para juntar argumentos y pasarlos a otra función, ¿es eso lo que quiere decir?

ftop=function(...) f(...) 
f=function(a,b) a[b] 

a=data.frame(b=10) 

ftop(a,"b") 

f(a,"b") 
+0

no del todo. No quiero usar puntos, quiero que los argumentos explícitos pasen a la función interna. –

Cuestiones relacionadas