R:如何在S3类中使用和扩展data.table

disho6za  于 2022-12-20  发布在  其他
关注(0)|答案(2)|浏览(150)

我想创建一个S3类来扩展data.table,方法是添加一些属性,这些属性将被该类的其他方法使用。在下面的示例中,我添加了一个属性colMeas,该属性保存带有度量的列的名称:

library(data.table)

myclass <- function(dt, colMeas) {

  stopifnot(data.table::is.data.table(dt))

  data.table::setattr(dt, "colMeas", colMeas)
  data.table::setattr(dt, "class", union("myclass", class(dt)))

}

is.myclass <- function(obj) inherits(obj, "myclass")

我有一个修改现有度量列的方法:

modCol <- function(obj, arg) {
  UseMethod("modCol")
}

# Modify the existing column
modCol.myclass <- function(obj, arg) {

  stopifnot(is.myclass(obj))
  stopifnot(is.numeric(arg))

  colMeas <- attr(obj, "colMeas")

  obj[,
      (colMeas) := get(colMeas) + arg]
}

和添加新列的方法:

addCol <- function(obj, arg) {
  UseMethod("addCol")
}

# Add a column
addCol.myclass <- function(obj, arg) {

  stopifnot(is.myclass(obj))
  stopifnot(is.numeric(arg))

  colMeas <- attr(obj, "colMeas")

  obj[,
      colNew := get(colMeas) + arg]

  data.table::setattr(obj, "colNew", "colNew")
}

我使用的一切如下:

library(data.table)
dt = data.table(x = 1:10,
                y = rep(1, 10))
myclass(dt, colMeas = "y")

modCol(dt, 10)
addCol(dt, 10)

其中:

> dt
     x  y colNew
 1:  1 11     21
 2:  2 11     21
 3:  3 11     21
 4:  4 11     21
 5:  5 11     21
 6:  6 11     21
 7:  7 11     21
 8:  8 11     21
 9:  9 11     21
10: 10 11     21

> attributes(dt)
$names
[1] "x"      "y"      "colNew"

$row.names
 [1]  1  2  3  4  5  6  7  8  9 10

$class
[1] "myclass"    "data.table" "data.frame"

$.internal.selfref
<pointer: 0x7f841e016ee0>

$colMeas
[1] "y"

$colNew
[1] "colNew"

这个问题更多的是关于R/S3的“教条”。在上面的方法中,我“就地”修改了data.table对象,并且我可以调用这些函数而不将结果赋给新对象。这是在S3类中处理data.table对象的正确方法吗?或者我应该将显式的return(obj)添加到所有函数中,然后像这样赋给结果:

dt = myclass(dt, colMeas = "y")
    
dt = modCol(dt, 10)
dt = addCol(dt, 10)

这不会导致dt对象的过度复制吗?

wqnecbli

wqnecbli1#

我会投 Yes 来修改它,也就是说,不需要捕获返回值。
(在思考这个回答的过程中,我改变了两次主意,但现在我确定了)。
在data.table中有几个函数可以修改对象,例如setnames(...)。这有明确的优先级。
在data.table代码库中还有一个通过引用工作的一般哲学,这是一个重要的特性,使它有别于data.frames
运用这种设计理念听起来是正确的。
注意:我认为以不可见的方式返回data.table对象还是不错的。

g0czyy6m

g0czyy6m2#

您的代码似乎依赖于union()的一个(AFAIK)未记录的属性,即它是顺序保持的。您可能希望“myclass”出现在类列表的第一位,以便dispatch accurately。因此,我建议您使用append()而不是union()更新类列表:
data.table::setattr(dt, "class", append(c("myclass"), class(dt)))

相关问题