Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`as.matrix` and `as.data.frame` S3 methods vs. S4 methods

Tags:

methods

r

s4

I noticed that defining as.matrix or as.data.frame as S3 methods for an S4 class makes e.g. lm (formula, objS4) and prcomp (object) work out of the box. This doesn't work if they are defined as S4 methods.

Why does it matter whether the methods are defined as S3 or S4 method?

Example for as.data.frame:

setClass ("exampleclass", representation (x = "data.frame"))
object <- new ("exampleclass", x = iris)

setMethod ("as.data.frame", signature="exampleclass", definition= function (x, ...) x@x )
## [1] "as.data.frame"

as.data.frame (object)
## Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
## 1            5.1         3.5          1.4         0.2     setosa
## 2            4.9         3.0          1.4         0.2     setosa
## 3            4.7         3.2          1.3         0.2     setosa
## ...snip...

lm (Petal.Length ~ Petal.Width, object)
## error in as.data.frame.default(data) : 
##   cannot coerce class 'structure("exampleclass", package = ".GlobalEnv")' into a data.frame

as.data.frame.exampleclass <- function (x, ...) x@x

lm (Petal.Length ~ Petal.Width, object)
## Call:
##   lm(formula = Petal.Length ~ Petal.Width, data = object)
## 
## Coefficients:
##   (Intercept)  Petal.Width  
## 1.084        2.230  

As the situation may be a bit complicated with lm where the coercion will only occur when the formula is evaluated in an environment constructed from the data, here is a more simple case whith the same behaviour:

setMethod ("as.matrix", signature="exampleclass", definition= function (x, ...) as.matrix (x@x[, 1:4]) )
prcomp (object)
## error in as.vector(data) : 
##   No method to coerce this S4 class into a vector
as.matrix.exampleclass <- function (x, ...) as.matrix (x@x [, 1:4])
prcomp (object)
##   Standard deviations:
##     [1] 2.0562689 0.4926162 0.2796596 0.1543862
##   
## Rotation:
##   PC1         PC2         PC3        PC4
## Sepal.Length  0.36138659 -0.65658877  0.58202985  0.3154872
## Sepal.Width  -0.08452251 -0.73016143 -0.59791083 -0.3197231
## Petal.Length  0.85667061  0.17337266 -0.07623608 -0.4798390
## Petal.Width   0.35828920  0.07548102 -0.54583143  0.7536574

Here, stats:::prcomp.default is called, which starts with a plain x <- as.matrix (x). This fails with the above S4 definition, but works with the S3 definition.

like image 326
cbeleites unhappy with SX Avatar asked Mar 23 '13 22:03

cbeleites unhappy with SX


1 Answers

I take it from the comments that lm simply calls as.data.frame explicitly. If you look at as.data.frame:

> as.data.frame
function (x, row.names = NULL, optional = FALSE, ...) 
{
    if (is.null(x)) 
        return(as.data.frame(list()))
    UseMethod("as.data.frame")
}
<bytecode: 0x29140b8>
<environment: namespace:base>

You'll see that it calls the S3 generic, and from the methods documentation

An S4 method alone will not be seen if the S3 generic function is called directly. However, >primitive functions and operators are exceptions: The internal C code will look for S4 >methods if and only if the object is an S4 object. In the examples, the method for [ for >class "myFrame" will always be called for objects of this class.

like image 131
jamie.f.olson Avatar answered Sep 28 '22 02:09

jamie.f.olson