Seb*_*ian 17 r s4 data.table
在R-Package data.table中,?data.table-class表示'data.table' 的手册条目可用于类定义中的继承,即在调用中的contains参数中setClass:
library("data.table")
setClass("Data.Table", contains = "data.table")
Run Code Online (Sandbox Code Playgroud)
但是,如果我创建一个Data.Table的实例,我本可以预期我可以将其视为data.table.事实并非如此.以下片段会导致错误,据我所知,这是因为该[.data.table函数无法处理S3和S4调度的混合:
dat <- new("Data.Table", data.table(x = 1))
dat[TRUE]
Run Code Online (Sandbox Code Playgroud)
我通过定义一个新的方法[并将任何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())
)
})
Run Code Online (Sandbox Code Playgroud)
一个构造函数让它感觉更舒服:
Data.Table <- function(...) new("Data.Table", data.table(...))
dat <- Data.Table(x = 1, key = "x")
dat[1]
Run Code Online (Sandbox Code Playgroud)
这对于某些场景是可以接受的,但是我放弃了data.table包中的所有get和set函数,我怀疑我销毁了其他一些功能.那么问题是如何实现一个有效的S4 data.table类?我将不胜感激
有对SO一个相关的问题,我发现,它提出了一个类似的方法.但是,我认为这将涉及太多编码以使其可行.
我认为简短的答案(这个问题仍然像提出时一样有效)是,不推荐使用 data.table 作为 S4 中的超类,并且如果没有大量的努力和某些不稳定风险,也是不可能的。
目前还不太清楚当前案例的目标应该是什么,但我们假设没有像分叉和修改现有包这样的替代方案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)
Run Code Online (Sandbox Code Playgroud)
现在要检查一下,正如您在 Github 存储库data.table.R[.data.table上看到的那样,有很多代码,因此只需以最简单的虚拟方式重现相关部分即可:
# 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)))
Run Code Online (Sandbox Code Playgroud)
我们遇到了错误,试图设置row.names,但它不起作用,因为dim(ans),因此nrow是NULL。
setattr(ans, "class", class(dat)),它效果不佳(尝试isS4(ans)或print(ans)之后)。事实上,?class我们可以读到关于S4的信息:该函数的替换版本将类设置为提供的值。对于具有正式定义的类,强烈反对以这种方式直接替换类。表达式as(object, value)是将对象强制为特定类的方法。
data.table's setattr,通过C使用R's setAttrib函数,类似于调用attr(ans, "class") <- "Data.Table"or class(ans) <- "Data.Table",这也会搞砸。
如果您setattr(ans, "class", class(dat2))这样做,您会发现这里一切都很好,就像 一样S3。不过,还要注意一点:
setattr(ans, "class", "data.frame")
Run Code Online (Sandbox Code Playgroud)
然后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")
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
723 次 |
| 最近记录: |