Home > Enterprise >  how to setMethod `[` for a S4 object to be applied to a data.table in a slot
how to setMethod `[` for a S4 object to be applied to a data.table in a slot

Time:07-30

I would like to be able to apply the "subset" (bracket) [ method on a S4 object let's call it foo in such a way that when it is called setMethod("[", 'foo', ... it will apply the [ operator on the data.table it holds in a specific slot.
Example:

foo <- setClass("foo", slots = c(myDT = "data.table"),  
                   prototype = prototype( myDT = NULL ))
# quickly make a foo class with a DT in the myDT slot 
myfoo <- new("foo", myDT = data.table(x=rep(c("b","a","c"),each=3), y=c(1,3,6), v=1:9))
# sneak peek
myfoo
An object of class "foo"
Slot "myDT":
   x y v
1: b 1 1
2: b 3 2
3: b 6 3
4: a 1 4
5: a 3 5
6: a 6 6
7: c 1 7
8: c 3 8
9: c 6 9

The tricky part

# I want to be able to do eg 
myfoo[1:3, 2:3]
   y v
1: 1 1
2: 3 2
3: 6 3

and have it give me the same result as if doing:

myfoo@myDT[1:3, 2:3]
   y v
1: 1 1
2: 3 2
3: 6 3

So far (I am guessing) it will/should be something along the lines of

setMethod(f = "[", signature = signature(x = "foo"),
                    definition = function(x, ...) {
                      `[`([email protected], ...)
                    # OR maybe 
                    # x <- x@myDT  
                    # callNextMethod(x, ...)
                 }

)

But whatever I call myfoo[i,j] with it wll always just return the whole data.table.

Any ideas if this can be accomplished? So far I am stuck usually on errors about j not fitting the bill. And I would like to avoid having to fully implement some form of shadow-indexing for this slot if I can somehow "recycle" what is available in data.table already;
of course also with the added benefit of the other data.table functions maybe also being applicable this way?
But for a beginning "passing on" indices would be a good start.

PS: If you wonder why not just do myfoo@myDT - the real life foo class has multiple slots of which only one (the data.table one) is "worthy" to be indexed and so I want to "shortcut" that methods application a bit.

CodePudding user response:

I found a way to achieve above task (by borrowing ideas from here).
Feels a bit "hacky" but maybe someone will still jump in (later) with a more elegant solution.

fooDT <- setClass("fooDT", contains = "data.table")

setGeneric("as.fooDT", function(x) standardGeneric("as.fooDT"))
setMethod("as.fooDT", signature = "foo",
          function(x) new("fooDT", [email protected])
)

setMethod(f = "[", signature = signature(x = "foo"),
          definition = function(x, ...) {
              x <- as.fooDT(x)
              callGeneric()
            }
) 

CodePudding user response:

If you created the foo class, is it "a data table with a few things tacked on"? If so, then consider redefining your class to extend the data class:

setClass("foo",slots=list(
    ##other slots besides data table
),extends="data.table") -> foo

Then any method defined for a data table will work for this (use [email protected] to get at the "data table part" of foo object x if you need to).

If not, then you can still define your own method to act on another slot, as follows:

setMethod("[",
  signature(x="foo",i="ANY",j="ANY"),
  def=function(x,i,j) x@myDT[i,j]
)

the reason this may not have worked before is that you didn't include i and j in the method signature. You may also need to define a method for signature x="foo",i="ANY",j="missing" to cover the case when j isn't specified at all.

  • Related