自定义继承自`data.frame`并包含替换方法的类

3
我定义了一个类(`tdtfile`),它继承了 `data.frame`。现在我正在尝试定义一个 `[.data.frame` 相当的替代方法,以返回一个适当的 `tdtfile` 类对象,而不是 `data.frame`,但我遇到了麻烦。
这是我正在做的事情:
# Define Class
setClass("tdtfile",
  representation(Comment = "character"),
   prototype(Comment = NULL),
   contains = c("data.frame"))

# Construct instance and populate
test <- new("tdtfile",Comment="Blabla")
df <- data.frame(A=seq(26),B=LETTERS)
for(sName in names(getSlots("data.frame"))){
  slot(test,sName) <- slot(df,sName)
}

# "Normal" data.frame behavior (loss of slot "Comment")
str(test[1])
# Works as well - will be trying to use that below
`[.data.frame`(test,1)

# Try to change replacement method in order to preserve slot structure 
# while accessing data.frame functionality
setMethod(
  `[`,
  signature=signature(x="tdtfile"),
  function(x, ...){
    # Save the original
    storedtdt <- x
    # Use the fact that x is a subclass to "data.frame"
    tmpDF <- `[.data.frame`(x, ...)
    # Reintegrate the results
    if(inherits(x=tmpDF,what="data.frame")){
      for(sName in names(getSlots("data.frame"))){
        slot(storedtdt,sName) <- slot(tmpDF,sName)
      }
      return(storedtdt)
    } else {
      return(tmpDF)
    }
  })

# Method does not work - data.frame remains complete. WHY?
str(test[1])

# Cleanup
#removeMethod(
#  `[`,
#  signature=signature(x="tdtfile"))

当调用类似以下的内容时:

tdtfile[1]

这将返回一个包含所有data.frame列而不仅仅是第一个的tdtfile对象...有人能发现我错过了什么吗?
谢谢你的帮助。
真诚地,Joh

嗨Joh,欢迎来到SO。您能详细说明一下您最后一句话吗?具体来说,您看到了什么,您希望看到什么呢? - Ricardo Saporta
感谢您查看此内容。我对示例进行了重大改进,使其完全自包含/可再现。我现在尝试做的事情和看到的是否更加清晰? - balin
1个回答

1
你的方法出现问题的原因是 ijdrop 在你的 [ 方法内部自动可用,我认为这只是 [ 通用功能工作方式的结果。这意味着你需要通过名称将这些参数传递给 [.data.frame,而不是依赖于 ...。不幸的是,这反过来又使你需要正确处理各种形式的索引。

下面是一个修改后的方法定义,虽然在某些使用 drop 参数的情况下可能不完全类似于纯数据框索引:

setMethod(
    `[`,
    signature=signature(x="tdtfile"),
    function(x, ...){
        # Save the original
        storedtdt <- x
        # Use the fact that x is a subclass to "data.frame"
        Nargs <- nargs()
        hasdrop <- "drop" %in% names(sys.call())
        if(Nargs==2) {
            tmpDF <- `[.data.frame`(x, i=TRUE, j=i, ..., drop=FALSE)
        } else if((Nargs==3 && hasdrop)) {
            tmpDF <- `[.data.frame`(x, i=TRUE, j=i, ..., drop)
        } else if(hasdrop) {
            tmpDF <- `[.data.frame`(x, i, j, ..., drop)
        } else {
            tmpDF <- `[.data.frame`(x, i, j, ...)
        }
        # Reintegrate the results
        if (inherits(x=tmpDF, what="data.frame")){
            for(sName in names(getSlots("data.frame"))){
                slot(storedtdt, sName) <- slot(tmpDF, sName)
            }
            return(storedtdt)
        } else {
            return(tmpDF)
        }
    })

以下是一些关于您的测试对象的例子:

> head(test[1])
Object of class "tdtfile"
  A
1 1
2 2
3 3
4 4
5 5
6 6
Slot "Comment":
[1] "Blabla"

> test[1:2,]
Object of class "tdtfile"
  A B
1 1 A
2 2 B
Slot "Comment":
[1] "Blabla"

我不确定是否有更规范的方法来做这个。也许可以尝试查看一些S4包的源代码?
编辑:这里有一种精神上类似于上面提取方法的替代方法。在直接调用[<-之前,此方法明确地将对象强制转换为数据框,主要是为了避免如果[<-.data.frame执行时出现警告。同样,行为与纯数据框替换方法并不完全相同,但通过更多的工作,它可以被改进。
setMethod(
    `[<-`,
    signature=signature(x="tdtfile"),
    function(x, ..., value){
        # Save the original
        storedtdt <- x
        # Use the fact that x is a subclass to "data.frame"
        Nargs <- nargs()
        if (any(!names(sys.call()) %in% c("", "i", "j", "value"))) {
            stop("extra arguments are not allowed")
        }
        tmpDF <- data.frame(x)
        if(Nargs==3) {
             if (missing(i)) i <- j
             tmpDF[i] <- value
        } else if(Nargs==4) {
             tmpDF[i, j] <- value
        }
        # Reintegrate the results
        for(sName in names(getSlots("data.frame"))){
            slot(storedtdt, sName) <- slot(tmpDF, sName)
        }   
        return(storedtdt)
    })

例子:

> test[2] <- letters
> test[1,"B"] <- "z"
> test$A[1:3] <- 99
> head(test)
Object of class "tdtfile"
   A B
1 99 z
2 99 b
3 99 c
4  4 d
5  5 e
6  6 f
Slot "Comment":
[1] "Blabla"

作为旁注,如果抽取/替换在数据框架上的工作需要 完全 与原样相同,我会考虑重写类,将包含数据框架的插槽作为超类,而不是 data.frame。组合优于继承!

问题文本提到了“替换”,但代码只涉及提取,所以我只使用了后者。 - regetz
谢谢您的输入...我会探索一下,但是您的评论似乎表明test[1] <- LETTERS或类似的方法仍然不起作用...就像我说的那样...调查后再回来... - balin
@balin:我添加了一个方法,可以实现你的替换示例。 - regetz
非常感谢您...请给我一些时间自己处理它 ;) - balin

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