[R] s3 methods on S4 objects

Martin Morgan mtmorgan at fhcrc.org
Thu Jan 13 19:33:33 CET 2011


On 01/13/2011 09:49 AM, steven mosher wrote:
>  
> 
> On Thu, Jan 13, 2011 at 6:46 AM, Martin Morgan <mtmorgan at fhcrc.org
> <mailto:mtmorgan at fhcrc.org>> wrote:
> 
>     On 01/12/2011 10:54 PM, steven mosher wrote:
>     > I have J Chambers wonderful text ( Software for data Analysis) and
>     I've been
>     > trying
>     > my hand at some very routine S4 OOP development.
>     >
>     > One of the things I was trying to do was to create some very basic S4
>     > classes. The first
>     > was simply a class that had a data.frame as part of its
>     representation.
>     >
>     > setClass("df",representation(dirframe="data.frame"))
>     >
>     > The object basically contains a data.frame that represents a file
>     directory
>     > listing
>     > with a column named filename, size, time, etc.
>     >
>     > And then I have methods for doing various things with this object.
>     >
>     > I then tried to tackle the problem of coercing this S4 object to a
>     > data.frame. Again just a learning exercise.
>     >
>     > The goal would be able to make a call like this
>     >
>     > testFrame <- as.data.frame(x)
>     >
>     > where x, was an object of class  "df"
>     >
>     > If I try to define "as.data.frame" as a S4 method, then I can make
>     it work,
>     > but I then destroy the S3 functionality
>     > of as.data.frame, so that if I were to try to coerce a matrix to a
>     > data.frame it would work.
> 
>     Hi Steven --
> 
>     This works for  me
> 
>     setClass("A", representation=representation(df="data.frame"))
> 
>     setMethod("as.data.frame", "A",
>        function(x, row.names=NULL, optional=FALSE, ...)
>     {
>        ## implementation, e.g.,
>        callGeneric(x at df, row.names=row.names, optional=optional, ...)
>     })
> 
> 
> this makes no sense to me. 
> 
> Looking at this in the manual:
> "A call to callGeneric can only appear inside a method definition. It
> then results in a call to the current generic function. The value of
> that call is the value of callGeneric. While it can be called from any
> method, it is useful and typically used in methods for group generic
> functions."

callGeneric is not necessary, and I should just have said

  as.data.frame(x at df, row.names=row.names, optional=optional, ...)

> I'm further confused. what is the "current" generic function?

I think of a generic 'foo' as a function, and inside that function are
functions foo,A-method, foo,B-method etc for classes A, B, .... When in
one of these methods, foo,A-method, the current generic is the function
'foo'. For as.data.frame, the generic is unambiguous ('as.data.frame' !)
and the interesting case are the group generics (?Logic, for instance),
where a single method

  setMethod("Logic", function(e1, e2) callGeneric(<YOUR CODE HERE>))

and you'll have in effect written 'methods' for all the operators
defined in the 'Logic' group. Cool.

> 
>     > as.data.frame(new("A"))
>     Object of class "data.frame"
>     data frame with 0 columns and 0 rows
>     > as.data.frame(matrix(0, 3, 5))
>      V1 V2 V3 V4 V5
>     1  0  0  0  0  0
>     2  0  0  0  0  0
>     3  0  0  0  0  0
> 
> 
>     Maybe you call setGeneric (no need to, setMethod will promote
>     as.data.frame automatically) in a way that does not specify the default
>     (arg useAsDefault) correctly?
> 
> 
>   I think that may have been the mistake.. what do you mean by
> no need to call setGeneric?

The only code I had was what was written above -- setClass and
setMethod. I could also have

  setGeneric("as.data.frame")

and would have been ok -- the default behavior of setGeneric in this
case is to make a generic function as.data.frame, AND a method
as.data.frame,ANY-method. The as.data.frame,ANY-method is implemented as
base::as.data.frame, and is where objects not handled by methods I
implement might end up being dispatched to. In a new R session, try

  setGeneric("as.data.frame")
  showMethods(as.data.frame)
  selectMethod(as.data.frame, "ANY")

If I had done

>  setGeneric("as.data.frame",
              function(x) standardGeneric("as.data.frame"))
Creating a generic for 'as.data.frame' in package '.GlobalEnv'
    (the supplied definition differs from and overrides the implicit generic
    in package 'base': Formal arguments differ: (x), (x, row.names,
optional, ...))
[1] "as.data.frame"

then I'm in trouble -- I've created a generic 'as.data.frame', but since
the signature of my generic differs from the signature of
base::as.data.frame, the default behavior does NOT create a
as.data.frame,ANY method.

Not sure if this helps or not...

Martin

> 
> I really like chambers book, but there are certain parts where the lack
> of simple examples
> really makes it difficult to follow.
> 
> 
>     Martin
> 
> 
>     >
>     >
>     > So, I guess my question is what do I do, write an s3 method for
>     > as.data.frame that takes  a "df" object as a paramter?
>     > The book wasn't exactly clear ( or I'm not that bright), or is
>     there a way
>     > to make the S4 method I wrote "as.data.frame"
>     > call the S3 method if needed?
>     >
>     >       [[alternative HTML version deleted]]
>     >
>     > ______________________________________________
>     > R-help at r-project.org <mailto: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.
> 
> 
>     --
>     Computational Biology
>     Fred Hutchinson Cancer Research Center
>     1100 Fairview Ave. N. PO Box 19024 Seattle, WA 98109
> 
>     Location: M1-B861
>     Telephone: 206 667-2793
> 
> 


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

Location: M1-B861
Telephone: 206 667-2793



More information about the R-help mailing list