我认为简短的回答(问题仍然与提出时一样有效)是在 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) 并且因此 nrow 是 NULL。
所以真正的问题在于setattr(ans, "class", class(dat)) 的使用,它不能很好地工作(稍后尝试isS4(ans) 或print(ans))。事实上,从?class我们可以了解到S4:
函数的替换版本将类设置为提供的值。对于具有正式定义的类,直接以这种方式替换类是强烈反对。 as(object, value) 表达式是将对象强制为特定类的方式。
data.table'ssetattr,通过C使用R'ssetAttrib函数,类似于调用attr(ans, "class") <- "Data.Table"或class(ans) <- "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")