[R] using setMethod or setGeneric to change S4 accessor symbol from @ to $

Martin Morgan mtmorgan at fhcrc.org
Tue Feb 9 00:43:51 CET 2010


On 02/08/2010 02:54 PM, Markus Weisner wrote:
> Thanks.  Used getGeneric("[") to figure out the general format for the
> setMethod, but am having some problem with how to set up the actual
> function:
> 
>> getGeneric("[")
> standardGeneric for "[" defined from package "base"
> 
> function (x, i, j, ..., drop = TRUE)
> standardGeneric("[", .Primitive("["))
> <environment: 0x116513c30>
> Methods may be defined for arguments: x, i, j, drop
> Use  showMethods("[")  for currently available ones.
> 
> Based on this, I set up the following code:
> 
> setClass("A", representation(a="numeric", b="numeric"))
> data = new("A", a=1:10, b=1:10)
> setMethod("[", "A",
>         function(x, i, j, ..., drop) {
>             slotnames <- slotNames(x)[j]
>             new_ = new("A")
>             for(slot in slotnames) new_data at slot = x at slot[i]
>             new_data
>         })
> data[5,c("a")]

probably there are several issues and covering them in an email response
won't do them justice.

instead of new_data at slot, use slot(new_data, slot)

"[" dispatches on four arguments, and likely the cases need to be
handled differently (e.g., data[,"a"] vs. data[,TRUE] vs data[1,]). So
you'll end up with methods

setMethod("[", c("A", "missing", "character", "ANY"), ...
setMethod("[", c("A", "missing", "logical", "ANY"), ...
setMethod("[", c("A", "numeric", "missing", "ANY"), ...

plus others, or you'll write something like

setMethod("[", c("A", "missing", "ANY", "ANY"),
          function(x, i, j, ..., drop=TRUE)
{
   if (is.character(j))
       j <- match(j, slotNames(x))
   j <- slotNames(x)[j]
   ...
})

("ANY" is implicit, it's unlikely you'll ever dispatch on 'drop', so a
signature for "[" often omits teh fourth signature element). You'll aim
for re-use, so likely the methods are all wrappers around some simple
function .subset_A(x, i, j, drop) where i, j are the types that'll work.

x at slot <- value and slot(x, slot) <- value make (at least) one copy of x
each time they're invoked, so your code above is making multiple copies
of the data. One strategy is not to define an  'initialize' method and
gain the benefit of the default method as a kind of copy constructor,
along the lines of

   initialize(x, a=slot(x, "a")[j], b=slot(x, "b")[j])

if the subset were to be of slots a and b.

You said your objective was to write a kind of enhanced data.frame, so
maybe a 'cheap' way to get the functionality you're after would be

  setClass("A", representation=representation(data="data.frame"))
  setMethod("[", c("A", "ANY", "ANY"),
            function(x, i, j, ..., drop=TRUE)
  {
       initialize(x, data=slot(x, "data")[i, j, drop=drop])
  })

Likely you'd have to write methods for when one or both of i, j were
'missing', and probably enforce drop=FALSE (since when drop=TRUE you
won't get a data.frame, and invalidate your class). You could use
setValidity to insist that the data.frame met your constraints.

> data = new("A", data=data.frame(a=1:10, b=10:1))
> data[1:5, 2, drop=FALSE]
An object of class "A"
Slot "data":
   b
1 10
2  9
3  8
4  7
5  6

There are a number of S4 packages that might provide good (or
otherwise!) examples, including Matrix, IRanges (which has a DataFrame
class, capable of holding anything with length() and [ defined) -- maybe
that's what you'd like to extend?) and Biobase (IRanges and Biobase are
in Bioconductor).

Martin

> 
> The problem is that I cannot access S4 object slots using @ and a character
> variable.  I also cannot access a slot using the typical brackets since that
> is what I am trying to define here.  Kind of stuck.  Thanks for any advice
> you might have.
> Best,
> Markus
> 
> On Mon, Feb 8, 2010 at 4:54 PM, Martin Morgan <mtmorgan at fhcrc.org> wrote:
> 
>> On 02/08/2010 01:22 PM, Markus Weisner wrote:
>>> Worked like a charm!!  Thank you so much.  I just plugged the following
>> into
>>> my code ...
>>>
>>> setMethod("$", "CADresponses", function(x, name) slot(x, name))
>>>
>>> ... and it worked perfect.  If you don't mind, I have a quick follow up
>>> question, using your example
>>>
>>> setClass("A", representation(a="numeric", b="numeric"))
>>> setMethod("$", "A", function(x, name) slot(x, name))
>>> data = new("A", a=1:10, b=1:10)
>>> data$a[5] #now works thanks to your code
>>> data$a[5] <- 200 #assignments do not work -- any ideas?
>>
>> same idea, but for "$<-"
>>
>>> setClass("A", representation(a="numeric"))
>> [1] "A"
>>> getGeneric("$<-")
>> standardGeneric for "$<-" defined from package "base"
>>
>> function (x, name, value)
>> standardGeneric("$<-", .Primitive("$<-"))
>> <environment: 0x14c33a8>
>> Methods may be defined for arguments: x, value
>> Use  showMethods("$<-")  for currently available ones.
>>> setReplaceMethod("$", "A", function(x, name, value) {
>> +     slot(x, name) <- value
>> +     x
>> + })
>> [1] "$<-"
>>> a <- new("A", a=1:10)
>>> a$a <- 10:1
>>> a
>> An object of class "A"
>> Slot "a":
>>  [1] 10  9  8  7  6  5  4  3  2  1
>>
>>> data[5,c("a")] = 200 #would also like this to work -- any ideas?
>>>
>>> Do you have any suggestions for getting assignments and brackets to work
>> as
>>> they would for data frames?  Thanks so much for your help.
>>
>> same approach, but using getGeneric("[") and getGeneric("[<-") to guide
>> you.
>>
>> Martin
>>
>>> Best,
>>> Markus
>>>
>>>
>>>
>>> On Mon, Feb 8, 2010 at 2:44 PM, Martin Morgan <mtmorgan at fhcrc.org>
>> wrote:
>>>
>>>> On 02/07/2010 08:31 PM, Markus Weisner wrote:
>>>>> I created some S4 objects that are essentially data frame objects.  The
>>>> S4
>>>>> object definitions were necessary to verify data integrity and force a
>>>>> standardized data format.  I am, however, finding myself redefining all
>>>> the
>>>>> typical generic functions so that I can still manipulate my S4 objects
>> as
>>>> if
>>>>> they were data frames ... I have used setMethod to set methods for
>>>> "subset",
>>>>> "head", and "tail".  I would like to use setMethod or setGeneric to
>>>> enable
>>>>> me to use object$slotname to access object at slotname for my S4 objects.
>>>>  Any
>>>>> advice is appreciated.  Thanks.
>>>>
>>>> Hi Markus --
>>>>
>>>>> setClass("A", representation(a="numeric"))
>>>> [1] "A"
>>>>> new("A")$a
>>>> Error in new("A")$a : $ operator not defined for this S4 class
>>>>> getGeneric("$")
>>>> standardGeneric for "$" defined from package "base"
>>>>
>>>> function (x, name)
>>>> standardGeneric("$", .Primitive("$"))
>>>> <environment: 0xa62028>
>>>> Methods may be defined for arguments: x
>>>> Use  showMethods("$")  for currently available ones.
>>>>> setMethod("$", "A", function(x, name) slot(x, name))
>>>> [1] "$"
>>>>> new("A", a=1:10)$a
>>>>  [1]  1  2  3  4  5  6  7  8  9 10
>>>>> new("A", a=1:10)$b
>>>> Error in slot(x, name) : no slot of name "b" for this object of class
>> "A"
>>>>
>>>> does that help?
>>>>
>>>> Martin
>>>>
>>>>> --Markus
>>>>>
>>>>>       [[alternative HTML version deleted]]
>>>>>
>>>>> ______________________________________________
>>>>> R-help at r-project.org mailing list
>>>>> https://stat.ethz.ch/mailman/listinfo/r-help
>>>>> PLEASE do read the posting guide
>>>> http://www.R-project.org/posting-guide.html
>>>>> and provide commented, minimal, self-contained, reproducible code.
>>>>
>>>>
>>>> --
>>>> Martin Morgan
>>>> Computational Biology / Fred Hutchinson Cancer Research Center
>>>> 1100 Fairview Ave. N.
>>>> PO Box 19024 Seattle, WA 98109
>>>>
>>>> Location: Arnold Building M1 B861
>>>> Phone: (206) 667-2793
>>>>
>>>
>>
>>
>> --
>> Martin Morgan
>> Computational Biology / Fred Hutchinson Cancer Research Center
>> 1100 Fairview Ave. N.
>> PO Box 19024 Seattle, WA 98109
>>
>> Location: Arnold Building M1 B861
>> Phone: (206) 667-2793
>>
> 


-- 
Martin Morgan
Computational Biology / Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N.
PO Box 19024 Seattle, WA 98109

Location: Arnold Building M1 B861
Phone: (206) 667-2793



More information about the R-help mailing list