【问题标题】:R: how to use and extend data.table in an S3 classR:如何在 S3 类中使用和扩展 data.table
【发布时间】:2021-06-20 15:18:06
【问题描述】:

我想创建一个扩展 data.table 的 S3 类,方法是添加将由该类的其他方法使用的属性。在下面的示例中,我添加了一个属性 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 对象吗?

【问题讨论】:

    标签: r data.table r-s3


    【解决方案1】:

    我会投票 Yes 来修改它,也就是说,不要让它有必要捕获返回的值。

    (我在考虑这个回复的过程中两次改变主意,但现在我确定了)。

    data.table 中有几个函数可以就地修改对象,例如setnames(...)。这有明确的优先级。

    data.table 代码库中还有一个通用哲学可以通过引用来工作,这是将它与 data.frames 区分开来的一个重要特性

    采用这种设计理念听起来是正确的做法。

    注意:我觉得隐式返回 data.table 对象还是不错的。

    【讨论】:

    • 谢谢!隐形返回对象是什么意思?函数末尾的显式return?
    • 糟糕,我愣住了。你的意思是使用invisible(obj) 而不是return(obj)。
    • 见data.table::setnames的源码,函数体以invisible(x)结尾。这是一个特殊操作,暂时使 x 不可见,从某种意义上说,它在返回后不打印。您仍然可以将其分配给某个地方,并且您仍然可以调用例如。 print( setnames(...) ) 无论如何都要打印它,但是当它不需要拾取返回值时,这是一个很好的姿态。这意味着您可以在不弄乱终端的情况下执行 setnames(x),并且仍然可以根据需要链接它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-20
    • 2021-07-24
    • 2017-12-02
    相关资源
    最近更新 更多