You've specified "expression" as the class of the second argument in this example method. The first example returns an error because
NewVar=Petal.Width*Petal.Length
is being interpreted as a named argument to myfun with value
Petal.Width*Petal.Length
that doesn't get the chance to be evaluated, because NewVar isn't an argument for this method or generic.
In the second example, I'm not sure what is going on with the closed curly braces, as my error differs from the one shown:
Error in myfun(iris, { :
error in evaluating the argument 'y' in selecting a method for function 'myfun': Error: object 'Petal.Width' not found
However, I receive no error and get the iris data.frame as output when I force your expression to be an expression object:
myfun(iris, expression(NewVar=Petal.Width*Petal.Length))
I think this only partially answers your question, because trivially returning the iris data.frame was not what you wanted. The expression is not being evaluated properly by transform(). I suspect you want the output to match exactly the output from the following hard-coded version:
transform(iris, NewVar=Petal.Width*Petal.Length)
Here is a short example evaluating the expression using eval
z <- expression(NewVar = Petal.Width*Petal.Length)
test <- eval(z, iris)
head(test, 2)
[1] 0.28 0.28
Here is a version that works for adding one variable column to the data.frame:
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" "Species" "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
Again, this implementation has essentially hard-coded that one, and only one, variable column will be added to the input data.frame, although the name of that variable column and the expression are arbitrary, and provided as an expression. I'm certain there is a better, more general answer that would evaluate the expression held in y as if it were a direct call in the transform() function, but I'm stumped at the moment what to use as the appropriate "inverse" function to expression( ).
There is always the standard ... , if you don't actually want to dispatch on y:
setGeneric('myfun', function(x, ...) standardGeneric('myfun'))
setMethod('myfun', 'data.frame', function(x, ...){
transform(x, ...)
})
And this works great. But your question was about actually dispatching on an expression object.
The following does not work, but I think it is getting closer. Perhaps someone can jump in and make some final tweaks:
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"
Essentially, the "NewVar=" piece of the expression was not passed to transform() when we called myfun().
After much trial and error, I figured out a way that works for real. First convert the expression object into a list with as.list(), then build up the call that you want with the marvelous
do.call()
The complete example looks like this:
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"
And the new data.frame object "test" has the "NewVar" column we wanted.
transform
in the question body yet.) – Exponibleexpression()
andas.expression()
didn't really help me further alas. Tried to get an expression from thematch.call()
, but also there I didn't get any further. Care to elaborate a bit further on the difference between both uses of expression? – Overlive