如何为S4类定义子集运算符?

25

我在为S4类定义[$[[子集操作符时遇到了困难。

有没有人可以为我提供一个基本的示例来定义这三个操作符的S4类?

2个回答

40

发现通用性,以便我们知道我们的目标是什么。

> getGeneric("[")
standardGeneric for "[" defined from package "base"

function (x, i, j, ..., drop = TRUE) 
standardGeneric("[", .Primitive("["))
<bytecode: 0x32e25c8>
<environment: 0x32d7a50>
Methods may be defined for arguments: x, i, j, drop
Use  showMethods("[")  for currently available ones.

定义一个简单的类

setClass("A", representation=representation(slt="numeric"))

实现一种方法。
setMethod("[", c("A", "integer", "missing", "ANY"),
    ## we won't support subsetting on j; dispatching on 'drop' doesn't
    ## make sense (to me), so in rebellion we'll quietly ignore it.
    function(x, i, j, ..., drop=TRUE)
{
    ## less clever: update slot, return instance
    ## x@slt = x@slt[i]
    ## x
    ## clever: by default initialize is a copy constructor, too
    initialize(x, slt=x@slt[i])
})

实际应用中:

> a = new("A", slt=1:5)
> a[3:1]
An object of class "A"
Slot "slt":
[1] 3 2 1

有不同的策略来支持(隐式)许多签名,例如您可能还想支持逻辑和字符索引值,可能是针对i和j的。最直接的方法是“门面”模式,其中每个方法都对一种常见的子集索引类型进行一些初步强制转换,例如integer,以允许重新排序和重复索引条目,然后使用callGeneric调用一个单一方法,该方法执行类的子集操作。
对于[[,没有任何概念上的区别,除了希望尊重返回内容而不是对象的另一个实例的语义,如[所暗示的那样。对于$,我们有
> getGeneric("$")
standardGeneric for "$" defined from package "base"

function (x, name) 
standardGeneric("$", .Primitive("$"))
<bytecode: 0x31fce40>
<environment: 0x31f12b8>
Methods may be defined for arguments: x
Use  showMethods("$")  for currently available ones.

并且

setMethod("$", "A",
    function(x, name)
{
    ## 'name' is a character(1)
    slot(x, name)
})

使用

> a$slt
[1] 1 2 3 4 5

12
谢谢Martin!这真的很有帮助(到了我违反“不要留下感谢评论因为会造成干扰”的规定的地步 :-) )。 - Kyle Brandt

9
我会像@Martin_Morgan建议的那样处理您提到的运算符。但是,我还想补充几点:
1)关于定义$操作符来访问S4插槽(除非您打算从存储在特定插槽中的数据框中访问列?),我会小心。一般建议编写访问器函数,如getMySlot()setMySlot()来获取所需的信息。您可以使用@操作符从这些插槽中访问数据,尽管get和set最适合作为用户界面。对于用户来说,使用$可能会令人困惑,因为他们可能期望得到一个数据框。有关这些问题的详细讨论,请参见Christophe Genolini的this S4教程。如果这不是您使用$的方式,请忽略我的建议(但是该教程仍然是一个很好的资源!)。
2)如果您正在定义[[[从另一个类继承,比如vector,您还需要定义el()(相当于[] [[1L]] 或来自子集[]的第一个元素)和length()。我目前正在编写一个从numeric继承的类,numeric方法将自动尝试使用这些函数从您的类中调用。如果该类用于更有限的或者是您自己的个人使用,则可能不会出现问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接