I'd like to create an S3 class that extends data.table by adding attributes that would be used by other methods of that class. In the example below I'm adding an attribute colMeas that holds the name of the column with the measurement:
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")
I have a method that modifies the existing measurement column:
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]
}
And a method that adds a new column:
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")
}
I'm using everything as follows:
library(data.table)
dt = data.table(x = 1:10,
y = rep(1, 10))
myclass(dt, colMeas = "y")
modCol(dt, 10)
addCol(dt, 10)
Which gives:
> 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"
The question is more about the R/S3 "doctrine". In the methods above I'm modifying the data.table object "in-place" and I can call these functions without assigning results to new objects. Is this a correct way of handling data.table objects in S3 classes? Or should I add explicit return(obj) to all functions and then assign the results like so:
dt = myclass(dt, colMeas = "y")
dt = modCol(dt, 10)
dt = addCol(dt, 10)
Wouldn't that lead to an excessive copying of the dt object?