11

我试图说服 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 调度方法?


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

4

2 回答 2

2

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

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] "花萼长度" "花萼宽度" "花瓣长度" "花瓣宽度" "物种" "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] "花萼长度" "花萼宽度" "花瓣长度" "花瓣宽度" "物种"

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

经过多次反复试验,我找到了一种真正可行的方法。首先使用 as.list() 将表达式对象转换为列表,然后使用 marvelous 构建您想要的调用

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”列。

于 2011-09-10T08:41:26.780 回答
1

这不是 S4,也不是参数评估,它是 R 无法判断您是要传递命名参数还是您的表达式的形式为 a=b。

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

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

我也觉得我对表达不够了解。

于 2011-08-09T17:08:29.110 回答