使S4对象充当S3类?

时间:2013-07-31 16:45:32

标签: r class s4

我想编写一个S4对象,以便它可以传递给只接受S3对象的方法。 (似乎setOldClass()可能与此有关,但documentation对我来说不清楚?)

e.g。一个最小的例子想象我有S3类和函数:

myS3 <- list(a = 1, b = 2)
class(myS3) <- "myS3class"
myS3function.myS3class <- function(x) x$a + x$b

我有S4对象

setClass("myS4class", representation(a = "numeric", b = "numeric"))
obj <- new("myS4class", a = 1, b = 2)

我有什么可以做的吗

myS3function.myS3class(obj) 

给了我同样的东西

myS3function.myS3class(myS3) 

通过仅修改S4类

编辑这种方法的基本原理是利用S3类的所有现有方法(通常可能来自其他软件包等),而无需重写它们。我意识到一种方法只是编写一个强制方法(setAs()),将我的S4对象转换为S3对象,但用户总是必须手动执行此步骤。 (虽然它有效,但我也有点不清楚使用setAs()将S4类带入S3类是不好的做法,而不是在S4类之间进行映射)。

从我阅读setOldClass的文档的方式来看,听起来这可以使S3对象像S4对象一样?那是对的吗?如果是这样,我的问题是,是否可以反过来(可能通过在S4类中设置prototype?)。

如果这是不可能的,那么解释为什么这会是一个坏主意的理由呢?

3 个答案:

答案 0 :(得分:2)

在S4类中添加一个方法,将其转换为S3类。

setGeneric(
  "as.myS3class", 
  function(object)
  {
    standardGeneric("as.myS3class")
  }  
)

setMethod(
  "as.myS3class",
  signature(object = "myS4class"),
  function(object)
  {
    structure(list(a = object@a, b = object@b), class = "myS3class")
  }
)

然后你可以像这样调用S3方法:

myS3function(as.myS3class(obj))

答案 1 :(得分:0)

如果你想重用两个 S3和S4类的一个函数,而不是改变它,你可以为$编写自己的定义:

f <- function(x, name)
slot(x, name)

setMethod("$", signature=c(x="myS4class"), definition=f)

myS3function.myS4class(obj)
# [1] 3

但这对我来说似乎很可疑。对于初学者,您可能还需要[[的类似方法,因为函数可以引用列表元素:

setMethod("[[", signature=c(x="myS4class", i="character"),
                definition=function(x, i) slot(x, i))

你也需要分配方法:

setMethod("$<-", signature=c(x="myS4class", value="numeric"),
                 definition=function(x, name, value) `slot<-`(x, name, check=TRUE, value))

setMethod("[[<-", signature=c(x="myS4class", i="character", value="numeric"),
                  definition=function(x, i, value) `slot<-`(x, i, check=TRUE, value))

但是你有数字引用的问题:

obj[[1]]
# Error in obj[[1]] : this S4 class is not subsettable

所以你需要另一种方法:

g <- function(x, i)
{
    slots <- names(getClass("myS4class")@slots)
    slot(x, slots[i])
}

setMethod("[[", signature=c(x="myS4class", i="numeric"), g)

总而言之,似乎需要做很多工作才能获得更多收益。

答案 2 :(得分:0)

成功的解决方案确实埋藏在setOldClass

的文档中
 ## Examples of S3 classes with guaranteed attributes
 ## an S3 class "stamped" with a vector and  a "date" attribute
 ## Here is a generator function and an S3 print method.
 ## NOTE:  it's essential that the generator checks the attribute classes
 stamped <- function(x, date = Sys.time()) {
     if(!inherits(date, "POSIXt"))
       stop("bad date argument")
     if(!is.vector(x))
       stop("x must be a vector")
     attr(x, "date") <- date
     class(x) <- "stamped"
     x
 }

 print.stamped <- function(x, ...) {
     print(as.vector(x))
     cat("Date: ",  format(attr(x,"date")), "\n")
 }

 ## Now, an S4 class with the same structure:
 setClass("stamped4", contains = "vector", representation(date = "POSIXt"))

 ## We can use the S4 class to register "stamped", with its attributes:
 setOldClass("stamped", S4Class = "stamped4")
 selectMethod("show", "stamped")
 ## and then remove "stamped4" to clean up
 removeClass("stamped4")

 someLetters <- stamped(sample(letters, 10),
                        ISOdatetime(2008, 10, 15, 12, 0, 0))

 st <- new("stamped", someLetters)
 st
 # show() method prints the object's class, then calls the S3 print method.

 stopifnot(identical(S3Part(st, TRUE), someLetters))

 # creating the S4 object directly from its data part and slots
 new("stamped", 1:10, date = ISOdatetime(1976, 5, 5, 15, 10, 0))

请注意,S4对象可以使用S3打印方法。让我感到惊讶的是,即使没有对selectMethod的额外调用,这也适用于为S3类而不是S4类定义的其他方法。我在此处使用ape::phylo对象的一个​​更详细的示例说明了这一点:http://carlboettiger.info/2013/10/07/nexml-phylo-class-extension.html