使用表达式作为参数调度S4方法

时间:2011-08-09 16:47:16

标签: r expression dispatch s4

我试图说服S4方法使用表达式作为参数,但我总是得到一个错误返回。一个简单的例子,说明了我在这里要做的事情:

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

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

如果我现在尝试:

> 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

如果我理解正确的话,似乎已经在通用中评估了参数。所以将表达式传递给方法似乎至少是棘手的。是否有可能使用表达式的S4调度方法?


编辑:改为变换,因为这是一个更好的例子。

2 个答案:

答案 0 :(得分:2)

您已在此示例方法中将“expression”指定为第二个参数的类。第一个示例返回错误,因为

NewVar=Petal.Width*Petal.Length

被解释为带有值

的myfun的命名参数
Petal.Width*Petal.Length

没有机会被评估,因为NewVar不是此方法或泛型的参数。

在第二个例子中,我不确定封闭的花括号是怎么回事,因为我的错误与显示的错误不同:

myfun错误(iris,{:   在选择函数'myfun'的方法时评估参数'y'时出错:错误:找不到对象'Petal.Width'

然而,当我强制你的表达式成为表达式对象时,我没有收到任何错误并将iris data.frame作为输出:

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

我认为这只能部分回答你的问题,因为通过简单地返回iris data.frame并不是你想要的。 transform()没有正确评估表达式。我怀疑你希望输出完全匹配以下硬编码版本的输出:

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

以下是使用eval

评估表达式的简短示例
z <- expression(NewVar = Petal.Width*Petal.Length)
test <- eval(z, iris)
head(test, 2)

[1] 0.28 0.28

这是一个适用于向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

同样,这个实现基本上是硬编码的,只有一个变量列将被添加到输入data.frame中,尽管该变量列的名称和表达式是任意的,并作为表达式提供。我确定有一个更好的,更通用的答案可以评估y中保存的表达式,就像它是transform()函数中的直接调用一样,但我现在难以理解为什么用作相应的“逆” “功能表达()。

总有标准......,如果你实际上不想在y上发送:

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

这很有效。但你的问题是关于实际调度表达式对象。

以下不起作用,但我认为它越来越近了。也许有人可以跳进去做一些最后的调整:

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”

基本上,当我们调用myfun()时,表达式的“NewVar =”部分没有传递给transform()。

经过多次试验和错误,我找到了一种适用于真实的方法。首先将表达式对象转换为带有as.list()的列表,然后使用奇妙的

构建所需的调用
do.call()

完整示例如下所示:

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"

新的data.frame对象“test”包含我们想要的“NewVar”列。

答案 1 :(得分:1)

它不是S4,也不是参数评估,它不能判断你是否要传递一个命名参数,或者你的表达式的形式为a = b。

如果查看“within”的帮助,它会使用大括号来确保将表达式解析为表达式。

我还认为在函数内部调用不会在函数的调用者中进行替换...

我也认为我对表达方式知之甚少。