8

我想编写一个 S4 对象,以便可以将其传递给仅采用 S3 对象的方法。(似乎setOldClass()可能与此有关,但从文档中我不清楚?)

例如,举个简单的例子,假设我有 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()我意识到一种方法是简单地编写一个强制方法((虽然它有效,但我也有点不清楚setAs()将 S4 类带到 S3 类而不是 S4 类之间的映射是否是不好的做法)。

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

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

4

3 回答 3

3

向您的 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))
于 2013-09-05T11:07:46.420 回答
2

一个成功的解决方案确实隐藏在以下文档中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. 我在这里用一个关于我的用例的更详细的例子来说明这一点:http ape::phylo: //carlboettiger.info/2013/10/07/nexml-phylo-class-extension.html

于 2013-10-07T23:00:54.793 回答
1

如果您想为 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)

总而言之,这似乎是很多工作却没有多少收获。

于 2013-07-31T18:23:22.643 回答