【问题标题】:How to use data.table as super class in S4如何在 S4 中使用 data.table 作为超类
【发布时间】:2015-11-19 07:06:38
【问题描述】:

在 R-Package data.table 中,?data.table-class 的手动条目说“data.table”可用于类定义中的继承,即在调用 setClass 的 contains 参数中:

library("data.table")
setClass("Data.Table", contains = "data.table")

但是,如果我创建一个 Data.Table 的实例,我会期望我可以像对待 data.table 一样对待它。事实并非如此。下面的sn-p会报错,据我了解,这是因为[.data.table函数不能处理S3和S4的混合调度:

dat <- new("Data.Table", data.table(x = 1))
dat[TRUE]

我解决了这个问题,为[ 定义了一个新方法,并在对其进行评估之前将任何 Data.Table 强制转换为 data.table。

setMethod(
  "[", 
  "Data.Table", 
  function(x, i, j, ..., drop = TRUE) {
    mc <- match.call()
    mc$x <- substitute(S3Part(x, strictS3 = TRUE))
    Data.Table(
      eval(mc, envir = parent.frame())
    )
  })

还有一个构造函数让你感觉更舒服:

Data.Table <- function(...) new("Data.Table", data.table(...))
dat <- Data.Table(x = 1, key = "x")
dat[1]

这在某些情况下是可以接受的,但我从 data.table 包中丢失了所有 get 和 set 函数,并且我怀疑我破坏了一些其他功能。所以问题是如何实现一个有效的 S4 data.table 类?我会很感激

  1. 指向类似尝试/项目的指针
  2. 用于实施的更好/替代解决方案/想法
  3. 关于上述解决方案在性能方面的任何建议

我找到了one related question on SO,它提供了类似的方法。但是,我认为这将涉及太多的编码,不可行。

【问题讨论】:

  • 我认为某些功能不起作用(包括[)是data.table 中的一个错误/缺失功能(并不是[.data.table 没有被调用,而是代码不足以处理您的情况),所以我建议在 github 上打开一个关于此的问题。

标签: r data.table s4


【解决方案1】:

我认为简短的回答(问题仍然与提出时一样有效)是在 S4 中使用 data.table 作为超类是不推荐的,并且如果没有大量的努力和某些不稳定的风险是不可能的。

目前还不清楚手头案例的目标应该是什么,但我们假设没有其他选择,比如分叉和修改现有的 data.table 包。

那么,为了说明上面提到的[的情况,我们先初始化例子:

# replicating some code from above
library("data.table")
Data.Table <- setClass("Data.Table", contains = "data.table")

dat <- Data.Table(data.table(x = 1))
dat[1]
> Error in if (n > 0) c(NA_integer_, -n) else integer() : 
    argument is of length zero

dat2 <- data.table(x = 1)

现在检查[.data.table,你可以在Github repo data.table.R 上看到很多代码,所以只需以最简单的虚拟方式复制相关部分:

# initializing output
ans = vector("list", 1)
# data (just one line of code as we have just one value in our example).
# desired subscript is row 1, but we have just one column as well.
ans[[1]] <- dat[[1]][1]
# add 'names' attribute
setattr(ans, "names", "x")
# set 'class' attribute
setattr(ans, "class", class(dat))
# set 'row.names'
setattr(ans, "row.names", .set_row_names(nrow(ans)))

我们遇到了错误,尝试设置 row.names,这不起作用,因为 dim(ans) 并且因此 nrowNULL

所以真正的问题在于setattr(ans, "class", class(dat)) 的使用,它不能很好地工作(稍后尝试isS4(ans)print(ans))。事实上,从?class我们可以了解到S4

函数的替换版本将类设置为提供的值。对于具有正式定义的类,直接以这种方式替换类是强烈反对as(object, value) 表达式是将对象强制为特定类的方式。

data.table'ssetattr,通过C使用R'ssetAttrib函数,类似于调用attr(ans, "class") &lt;- "Data.Table"class(ans) &lt;- "Data.Table",同样会搞砸。

如果您改为使用setattr(ans, "class", class(dat2)),您会发现这里一切正常,S3 也应该如此。 不过还有一点要注意:

setattr(ans, "class", "data.frame")

然后print(ans)dim(ans) 对你来说可能看起来不太好...(尽管ans$x 没问题)。


以一种好的方式覆盖setattr() 也不是微不足道的,而且这种方法可能不会比您上面概述的方法更进一步。结果可能是这样的:

setattr_new <- function(x, name, value) {
  if (name == "class" && "Data.Table" %in% value) {
    value <- c("data.table", "data.frame")
  }
  if (name == "names" && is.data.table(x) && length(attr(x, "names")) && !is.null(value))
    setnames(x, value)
  else {
    ans = .Call(Csetattrib, x, name, value)
    if (!is.null(ans)) {
      warning("Input is a length=1 logical that points to the same address as R's global TRUE value. Therefore the attribute has not been set by reference, rather on a copy. You will need to assign the result back to a variable. See https://github.com/Rdatatable/data.table/issues/1281 for more.")
      x = ans
    }
  }
  if (name == "levels" && is.factor(x) && anyDuplicated(value)) 
    .Call(Csetlevels, x, (value <- as.character(value)), unique(value))
  invisible(x)
}

godmode:::assignAnywhere("setattr", setattr_new)

identical(dat[1], dat2[1])
[1] TRUE

# then possibly convert back to S4 class if desired for further processing at the end
as(dat[1], "Data.Table")

【讨论】:

  • 感谢您的解释。我对您的理解是否正确,您建议分叉项目然后替换上面的行(如解释)?还是您主要想解释问题以更接近解决方案?看看现在和过去的 data.table,我认为对 S4 的支持并不现实。
  • 我同意你的最后一句话,特别是如果你想要充分的灵活性。保持与基本data.table 包中相同程度的复制也可能变得难以避免,并且您可能会失去性能。我的解释可能更接近于一个结论而不是一个解决方案。尽管如此,我在我的答案中添加了一个部分,显示了覆盖的工作方式。将分叉评论重新表述为一个问题:您想通过创建 S4 类添加什么?额外的插槽?在两个参数上调度的方法?这也会影响任何可能的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-12
  • 2018-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-30
相关资源
最近更新 更多