2011-08-09 13 views
11

Estoy intentando convencer a un método S4 para usar una expresión como argumento, pero yo siempre conseguir devuelve un error. Un ejemplo trivial que ilustra un poco lo que estoy tratando de hacer aquí:despacho métodos S4 con una expresión como argumento

setGeneric('myfun',function(x,y)standardGeneric('myfun')) 

setMethod('myfun',c('data.frame','expression'), 
      function(x,y) transform(x,y)) 

Si Ahora trato:

> myfun(iris,NewVar=Petal.Width*Petal.Length) 
Error in myfun(iris, NewVar = Petal.Width * Petal.Length) : 
    unused argument(s) (NewVar = Petal.Width * Petal.Length) 

> myfun(iris,{NewVar=Petal.Width*Petal.Length}) 
Error in myfun(iris, list(NewVar = Petal.Width * Petal.Length)) : 
error in evaluating the argument 'y' in selecting a method for 
function 'myfun': Error: object 'Petal.Width' not found 

Parece que los argumentos se evalúan en el genérica ya si entiendo bien . Así que pasar las expresiones a los métodos parece al menos complicado. ¿Existe la posibilidad de utilizar métodos de envío S4 utilizando expresiones?


editar: cambiado para transformar, ya que es un mejor ejemplo.

+1

Véase la página 399-402 de SDA, donde Chambers describe por qué se debe evaluar cualquier argumento probado por firmas. –

+0

@DWin: ¿quieres compartir un enlace? sfDA suena desconocido para mí, y creo que no está hablando de la Administración Estatal de Alimentos y Medicamentos ... –

+0

Lo siento. "Software para Análisis de Datos" por John Chambers. Como un punto adicional noté que sus valores de firma no estaban asociados con nombres de argumento, mientras que en los ejemplos ese es el formato. (Nota: aún no ha cambiado a 'transformar' en el cuerpo de la pregunta) –

Respuesta

2

Usted ha especificado "expresión" como la clase del segundo argumento en este ejemplo de método. El primer ejemplo se devuelve un error porque

NewVar=Petal.Width*Petal.Length 

se interpreta como un argumento con nombre de MYFUN con valor

Petal.Width*Petal.Length 

que no tienen la oportunidad de ser evaluados, debido newvar no es un argumento para este método o genérico.

En el segundo ejemplo, no estoy seguro de lo que está pasando con las llaves cerradas, ya que mi error es diferente del que se muestra:

error en myfun (iris, {: error en la evaluación de la argumento 'y' al seleccionar un método para la función 'myfun': Error: objeto 'Petal.Width' no encontrado

Sin embargo, no recibo ningún error y obtengo el iris data.frame como salida cuando forzo su expresión a ser un objeto de expresión:

myfun(iris, expression(NewVar=Petal.Width*Petal.Length)) 

Creo que esto solo responde parcialmente a su pregunta, porque devolver trivialmente el data.frame del iris no era lo que quería. La expresión no está siendo evaluada adecuadamente por transform(). Sospecho desea que la salida para que coincida exactamente la salida de la siguiente versión modificable:

transform(iris, NewVar=Petal.Width*Petal.Length) 

Aquí es un ejemplo corto evaluación de la expresión usando eval

z <- expression(NewVar = Petal.Width*Petal.Length) 
test <- eval(z, iris) 
head(test, 2) 

[1] 0,28 0,28

Aquí está una versión que funciona para añadir una columna variable a los datos.marco:

setGeneric('myfun',function(x,y)standardGeneric('myfun')) 
setMethod('myfun',c('data.frame','expression'), function(x,y){ 
    etext <- paste("transform(x, ", names(y), "=", as.character(y), ")", sep="") 
    eval(parse(text=etext)) 
}) 
## now try it. 
test <- myfun(iris, expression(NewVar=Petal.Width*Petal.Length)) 
names(test) 

[1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "especie" "newvar"

head(test) 

    Sepal.Length Sepal.Width Petal.Length Petal.Width Species NewVar 
1   5.1   3.5   1.4   0.2 setosa 0.28 
2   4.9   3.0   1.4   0.2 setosa 0.28 

Una vez más, esta implementación tiene esencialmente está codificado que una, y solo una, columna de variable se agregará a la entrada data.frame, aunque el nombre de esa columna de variable y la expresión son arbitrarios y se proporcionan como una expresión. Estoy seguro de que hay una respuesta mejor, más general, que evaluaría la expresión contenida en y como si fuera una llamada directa en la función transform(), pero por el momento estoy perplejo de qué utilizar como el "inverso" apropiado. "función a expresión().

siempre existe la norma ..., si es que en realidad no quiere despachar en Y:

setGeneric('myfun', function(x, ...) standardGeneric('myfun')) 
setMethod('myfun', 'data.frame', function(x, ...){ 
    transform(x, ...) 
}) 

Y esto funciona muy bien. Pero su pregunta era acerca de despachar realmente en un objeto de expresión.

Lo siguiente no funciona, pero creo que se está acercando. Tal vez alguien pueda entrar y hacer algunos ajustes finales:

setGeneric('myfun', function(x, y) standardGeneric('myfun')) 
setMethod('myfun',c('data.frame', 'expression'), function(x, y){ 
    transform(x, eval(y, x, parent.frame())) 
}) 
## try out the new method 
z <- expression(NewVar = Petal.Width*Petal.Length) 
test <- myfun(iris, z) 
names(test) 

[1] "Sepal.Length" "Sepal.Width" "" "Petal.Length Petal.Width" "Species"

En esencia, el elemento "NewVar =" de la expresión no se pasó a transform() cuando llamamos a myfun().

Después de mucho ensayo y error, descubrí una manera que funciona de verdad. En primer lugar convertir el objeto de expresión en una lista con as.list(), y luego construir la llamada que desea con el magnifico

do.call() 

El ejemplo completo es el siguiente:

setGeneric('myfun', function(x, y) standardGeneric('myfun')) 
setMethod('myfun',c('data.frame', 'expression'), function(x, y){ 
    do.call("transform", c(list(x), as.list(y))) 
}) 
# try out the new method 
z <- expression(NewVar = Petal.Width*Petal.Length) 
test <- myfun(iris, z) 
names(test) 
[1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species"  
[6] "NewVar" 

Y el nuevo El objeto "prueba" data.frame tiene la columna "NewVar" que queríamos.

+1

eval plantea un nuevo conjunto de problemas cuando tiene funciones anidadas. Entornos ... Sé que puedo codificarlo, pero el objetivo es, básicamente, tener la flexibilidad de la transformación, pero para mis propios objetos S4. Lo resolví usando los métodos S3, pero estaba especialmente interesado en lo que sucedía detrás de las pantallas ... –

+1

Sí, estaba encontrando el mismo problema con eval. Creo que el enfoque do.call que adjunté podría funcionar para su problema. –

+1

La mejor solución que pude encontrar, así que ahí tienes. –

1

No es S4, o la evaluación argumento, la que R no puede decir si se refiere a pasar un parámetro con nombre o si su expresión es de la forma a = b.

Si nos fijamos en la ayuda para el "interior", que utiliza llaves para asegurarse de que la expresión se analiza como una expresión.

También pienso llamar dentro dentro de una función no va a hacer la sustitución en la persona que llama de la función ...

También pienso que no sé lo suficiente sobre expresiones.

+0

¿No son las llaves solo porque el 'expr' es una expresión de varias líneas en el sentido de expresiones múltiples? No necesita llaves en 'within()' si proporciona una sola expresión R. –

+0

Su punto con respecto a parámetro o expresión con nombre es interesante. ¿Es este un momento donde la desigualdad de '=' y '<-' como asignación está mordiendo a @Joris en el culo? IIRC debe usar '<-' en' within() ', ya que' = 'no funciona como asignación allí. –

+0

Pruébalo: no funciona. Lo mismo para usar '<-'. –

Cuestiones relacionadas