R:如何在 S3 class 中使用和扩展 data.table
R: how to use and extend data.table in an S3 class
我想创建一个 S3 class,它通过添加 class 的其他方法可以使用的属性来扩展 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 classes 中处理 data.table
对象的正确方法吗?或者我应该向所有函数添加显式 return(obj)
,然后像这样分配结果:
dt = myclass(dt, colMeas = "y")
dt = modCol(dt, 10)
dt = addCol(dt, 10)
这不会导致对 dt
对象的过度复制吗?
我会投票 赞成 就地修改它,也就是说,不需要捕捉 returned 值。
(在考虑这个回复的过程中,我改变了两次想法,但现在我确定了)。
data.table 中有几个函数可以就地修改对象,例如 setnames(...)
。这有明确的优先级。
data.table 代码库中也有一个通用的哲学,通过引用工作,这是一个重要的特征,使它有别于 data.frames
采用这种设计理念听起来是正确的做法。
注意:我认为return data.table 对象仍然很好。
我想创建一个 S3 class,它通过添加 class 的其他方法可以使用的属性来扩展 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 classes 中处理 data.table
对象的正确方法吗?或者我应该向所有函数添加显式 return(obj)
,然后像这样分配结果:
dt = myclass(dt, colMeas = "y")
dt = modCol(dt, 10)
dt = addCol(dt, 10)
这不会导致对 dt
对象的过度复制吗?
我会投票 赞成 就地修改它,也就是说,不需要捕捉 returned 值。
(在考虑这个回复的过程中,我改变了两次想法,但现在我确定了)。
data.table 中有几个函数可以就地修改对象,例如 setnames(...)
。这有明确的优先级。
data.table 代码库中也有一个通用的哲学,通过引用工作,这是一个重要的特征,使它有别于 data.frames
采用这种设计理念听起来是正确的做法。
注意:我认为return data.table 对象仍然很好。