2013-07-31 42 views
8

我想寫一個S4對象,以便它可以傳遞給只有一個S3對象的方法。 (看起來像setOldClass()可能與此有關,但從documentation我不清楚?)使S4對象充當S3類嗎?

例如,一個小例子,想象我有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?)。

如果這是不可能的,那麼解釋爲什麼這將是一個壞主意?

回答

2

向您的S4課程添加一個方法,將其轉換爲S3課程。

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

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

然後你就可以調用S3方法是這樣的:

myS3function(as.myS3class(obj)) 
+0

感謝您的回答,但正如我上面所述,我真的在尋找一種解決方案,可以在不明確地調用轉換到S3的情況下工作。 (例如,看起來我們可以做相反的事情 - 有一個S4方法可以在沒有明確強制的情況下對S3對象進行操作)如果這是不可能的,我想明白爲什麼不更好一點.... – cboettig

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) 

全部起來,它似乎很多工作沒有太大的收穫。

+0

我知道我可以這樣做,但這不是我正在尋找的。特別是,我正在尋找一種不涉及修改或複製現有S3方法的解決方案(對不起,我應該已經更清楚了,在我的問題中我只說:「只修改S4課程」)。 – cboettig

+0

重寫了答案。不過,我認爲你最好寫自己的方法。 –

+0

感謝您的幫助。我同意重寫像「$」這樣的基本方法聽起來不是一個好的方法。雖然重寫類的每一種方法並不是一個解決方案 - 數十個包爲對象實現了100個函數,並且新的函數仍然被添加。這個流行課程的S4版本有很好的理由,但是如果沒有向後兼容性,它就不會發生。 – cboettig

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打印方法。讓我感到驚訝的是,這對於爲S3類定義的其他方法,但不適用於S4類而不需要額外調用selectMethod。我用一個更詳細的例子來說明這一點,我的用例是ape::phylo對象在這裏:http://carlboettiger.info/2013/10/07/nexml-phylo-class-extension.html