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

发布于 2024-11-28 20:19:48 字数 794 浏览 1 评论 0原文

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


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

I'm trying to convince an S4 method to use an expression as an argument, but I always get an error returned. A trivial example that illustrates a bit what I'm trying to do here :

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

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

If I now try :

> 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

It seems the arguments are evaluated in the generic already if I understand it right. So passing expressions down to methods seems at least tricky. Is there a possibility to use S4 dispatching methods using expressions?


edit : changed to transform, as it is a better example.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

日裸衫吸 2024-12-05 20:19:48

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

NewVar=Petal.Width*Petal.Length

is 被解释为 myfun 的命名参数,其值

Petal.Width*Petal.Length

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

在第二个示例中,我不确定闭合大括号发生了什么,因为我的错误与显示的错误不同:

Error in myfun(iris, { :
在为函数“myfun”选择方法时评估参数“y”时出错:错误:未找到对象“Petal.Width”

但是,当我强制表达式为表达式对象:

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

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

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

评估表达式的简短示例

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

这是一个使用 eval [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"

本质上,表达式的 "NewVar=" 部分不是当我们调用myfun()时传递给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”列通缉。

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.

浮世清欢 2024-12-05 20:19:48

这不是 S4,也不是参数求值,而是 R 无法判断您是否要传递命名参数,或者您的表达式是否采用 a=b 的形式。

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

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

我还认为我对表达式了解不够。

It's not S4, or the argument evaluation, its that R can't tell if you mean to pass a named parameter or if your expression is of the form a=b.

If you look at the help for "within", it uses curly brackets to make sure the expression is parsed as an expression.

I also think calling within inside a function won't do the replacement in the function's caller...

I also think I don't know enough about expressions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文