[R] S4 method dispatch with inheritance

Martin Maechler maechler at stat.math.ethz.ch
Mon Jul 20 22:45:05 CEST 2009


>>>>> "MH" == Michael Hahsler <michael at hahsler.net>
>>>>>     on Mon, 20 Jul 2009 08:57:28 -0500 writes:

    MH> Hi, I'm trying to create a new S4 class (myMatrix) which
    MH> for now just extends dgCMatrix (from package
    MH> Matrix). Then I want to use "[" which is defined in
    MH> Matrix.

    MH> Out of the box with "[" (defined in Matrix) I lose the
    MH> class information and the result is an object of class
    MH> dgCMatrix. 

Yes, and that's the case for all such methods for "extended"
classes, for S3 / S4 methods alike.
Yes, this is sometimes not what you would like, and on R-devel
we recently had proposals on definining special attributes (for
dataframe / matrix like objects) that
would automatically be preserved in "[i,j]" subsetting.... so
others had had similar wishes / dreams.

As I have learned in similar experience, it's not a good idea to
expect that this happens automatically.
Assume "myMatrix" was meant to always be a square matrix, or
always a symmetric matrix.  Then, only *some* "[i,j]" operations
would return a valid "myMatrix"...
For such reasons, indeed, it's you who must provide methods for
your class.  Of couese these methods will typically call the
"upper class" method, possibly via  callNextMethod(.), or
{my historical preference} directly.


    MH> If I specify a "["-method for myMatrix, it is
    MH> not used because a signature from Matrix seems to fit
    MH> better. 

Yes, Matrix" has many methods for "[", for good reasons,
but if you define your methods "correctly", 
things are solved easily, see below

    MH> However, the most important part of the
    MH> signature is the class of x (all else have ANY). Is
    MH> there a way to specify a single "["-method do make it
    MH> work for myClass?

a single method is not sufficient;  "["  is a generic
function with several signauture arguments, on which dispatch
can happen.
And note, that "[" is particularly delicate :

The code (well, the one in "Matrix") must be able to distinguish
between  M[ i,]
and	 M[ i ]  

Here's a working solution:
---------------------------------------------------------------

library("Matrix")

setClass("myMatrix", contains="dgCMatrix")

M <- as(Diagonal(x = 1:7), "CsparseMatrix")
(my <- as(as(M, "dgCMatrix"), "myMatrix"))

## here I lose the class "myMatrix"
class(my[1:2,])
## [1] "dgCMatrix"

## make sure [ keeps the class

if(FALSE)# not really needed
setMethod("[", signature(x = "myMatrix"), ## (ANY,ANY...)
          function(x, i, j, ..., drop)
          as(as(x,"dgCMatrix")[i, j, ..., drop], "myMatrix"))

setMethod("[", signature(x = "myMatrix", i="index", j="index", drop="logical"), 
          function(x, i, j, ..., drop)
          as(as(x,"dgCMatrix")[i, j, drop=drop], "myMatrix"))
setMethod("[", signature(x = "myMatrix", i="index", j = "missing", drop="logical"), 
          function(x, i, j, ..., drop)
          as(as(x,"dgCMatrix")[i, , drop=drop], "myMatrix"))
setMethod("[", signature(x = "myMatrix", i = "missing", j="index",drop="logical"), 
          function(x, i, j, ..., drop)
          as(as(x,"dgCMatrix")[ , j, drop=drop], "myMatrix"))


head(my, 2)       ## perfect
my[3,3,drop=FALSE] # ditto
my[, 2:4]          # ditto

---------------------------------------------------------------

Note that the three method definitions use slightly different
function bodies: When an argument is "missing", it's not a good
idea to pass it again to the generic "["  which then again would
like to do method dispatch on that missing...

Also, if you replace "index" (the indexing class introduced in
'Matrix", but more generally interesting) by "ANY",
you need another methods definition in order to match to you
"myMatrix" class first, before any of the many specific methods
in "Matrix".....

Note further, that at the DSC 2007, I already noted a similar
situation and had prposed back then, that it would be desirable
to allow the programmer to more closely to specify method
dispatch in a case like yours:  We would want to specify that
for method dispatch, the class of "x" (i.e. "myMatrix" in your case)
should weight much more than the classes of (i,j,drop).
But something like this has to wait for the future, .....

I hope this helped so far.



    >> library("Matrix")

    [.......]

    >> 
    >> setClass("myMatrix",
    MH> +     contains="dgCMatrix"
    MH> + )

 {{why 3 lines instead of 1 ?}}

    MH> [1] "myMatrix"
    >> 
    >> my <- as(as(rbind(1:10,1:10,1:10), "dgCMatrix"), "myMatrix")
    >> 
    >> ## here I lose the class "myMatrix"
    >> class(my[1:2,])
    MH> [1] "dgCMatrix"
    MH> attr(,"package")
    MH> [1] "Matrix"
    >> 
    >> ## make sure [ keeps the class
    >> setMethod("[", signature(x = "myMatrix", i = "ANY", j = "ANY",
    MH> +             drop = "ANY"),
    MH> +         function(x, i, j, ..., drop) {
    MH> +             x<- as(x, "dgCMatrix")[i, j, ..., drop]
    MH> +             as(x, "myMatrix")
    MH> +         })
    MH> [1] "["
    >> 
    >> ## and now it does not use the method defined above.
    >> class(my[1:2,])
    MH> Note: Method with signature "Matrix#index#missing#missing" chosen for 
    MH> function "[",
    MH> target signature "myMatrix#integer#missing#missing".
    MH> "myMatrix#ANY#ANY#ANY" would also be valid
    MH> Note: Method with signature "sparseMatrix#index#missing#logical" chosen 
    MH> for function "[",
    MH> target signature "myMatrix#integer#missing#logical".
    MH> "myMatrix#ANY#ANY#ANY" would also be valid
    MH> [1] "dgCMatrix"
    MH> attr(,"package")
    MH> [1] "Matrix"


    >> sessionInfo()
    MH> R version 2.9.1 (2009-06-26)
    MH> i486-pc-linux-gnu

    MH> locale:
    MH> LC_CTYPE=en_US.UTF-8;LC_NUMERIC=C;LC_TIME=en_US.UTF-8;LC_COLLATE=en_US.UTF-8;LC_MONETARY=C;LC_MESSAGES=en_US.UTF-8;LC_PAPER=en_US.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=en_US.UTF-8;LC_IDENTIFICATION=C

    MH> attached base packages:
    MH> [1] stats     graphics  grDevices utils     datasets  methods   base

    MH> other attached packages:
    MH> [1] Matrix_0.999375-29 lattice_0.17-25

    MH> loaded via a namespace (and not attached):
    MH> [1] grid_2.9.1

    MH> -- 
    MH> Michael Hahsler
    MH> email: michael at hahsler.net
    MH> web: http://michael.hahsler.net

    MH> ______________________________________________
    MH> R-help at r-project.org mailing list
    MH> https://stat.ethz.ch/mailman/listinfo/r-help
    MH> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
    MH> and provide commented, minimal, self-contained, reproducible code.




More information about the R-help mailing list