[Rd] Proper way to define cbind, rbind for s4 classes in package
Mario Annau
mario.annau at gmail.com
Mon Feb 9 23:28:03 CET 2015
Hi Michael,
I've tested your change in r67699 (using r67773) and the function now
correctly dispatches to r/cbind2 within the R-session without
bind_activation(TRUE). However, running unit tests using R CMD check I
figured out that the same function call delegates to r/cbind.matrix
(function uses S4 class as first- and matrix as second argument). Is
this a bug and/or how can I get function dispatch right (to r/cbind2)
for my test cases?
best,
mario
Am 02/02/15 um 12:32 schrieb Martin Maechler:
>>>>>> Michael Lawrence <lawrence.michael at gene.com>
>>>>>> on Sun, 1 Feb 2015 19:23:06 -0800 writes:
>
> > I've implemented the proposed changes in
> > R-devel. Minimally tested, so please try it. It should
> > delegate to r/cbind2 when there is at least one S4
> > argument and S3 dispatch fails (so you'll probably want to
> > add an S3 method for your class to introduce a conflict,
> > otherwise it will dispatch to cbind.data.frame if one of
> > the args is a data.frame). There may no longer be a need
> > for cBind() and rBind().
>
> > Michael
>
> This sounds great! Thank you very much, Michael!
> :-) :-)
>
> ... but .... :-( experiments with the Matrix package (and R
> devel with your change), show a remaining buglet with treating of dimnames :
>
> > M1 <- Matrix(m1 <- matrix(1:12, 3,4))
> > cbind(m1, MM = -1)
> MM
> [1,] 1 4 7 10 -1
> [2,] 2 5 8 11 -1
> [3,] 3 6 9 12 -1
> > cbind(M1, MM = -1) ## ---- notice the "..."
> 3 x 5 Matrix of class "dgeMatrix"
> ...
> [1,] 1 4 7 10 -1
> [2,] 2 5 8 11 -1
> [3,] 3 6 9 12 -1
> > rbind(R1 = 10:11, m1)
> [,1] [,2] [,3] [,4]
> R1 10 11 10 11
> 1 4 7 10
> 2 5 8 11
> 3 6 9 12
> > rbind(R1 = 10:11, M1) ## --- notice the 'deparse.level'
> 4 x 4 Matrix of class "dgeMatrix"
> [,1] [,2] [,3] [,4]
> deparse.level 10 11 10 11
> 1 4 7 10
> 2 5 8 11
> 3 6 9 12
> >
>
> Also, it seems you are not observing the 'deparse.level'
> argument at all:
> Looking at the last three lines of the example in ?cbind,
>
> rbind(1:4, c = 2, "a++" = 10, dd, deparse.level = 0) # middle 2 rownames
> rbind(1:4, c = 2, "a++" = 10, dd, deparse.level = 1) # 3 rownames (default)
> rbind(1:4, c = 2, "a++" = 10, dd, deparse.level = 2) # 4 rownames
>
> but using a Matrix matrix 'dd', we see that (row)names
> construction needs to amended:
>
> > (dd <- Matrix(rbind(c(0:1,0,0))))
> 1 x 4 sparse Matrix of class "dgCMatrix"
>
> [1,] . 1 . .
>
> > rbind(1:4, c = 2, "a++" = 10, dd, deparse.level = 0) # middle 2 rownames
> 4 x 4 sparse Matrix of class "dgCMatrix"
>
> deparse.level 1 2 3 4
> c 2 2 2 2
> a++ 10 10 10 10
> . 1 . .
> > rbind(1:4, c = 2, "a++" = 10, dd, deparse.level = 1) # 3 rownames (default)
> 4 x 4 sparse Matrix of class "dgCMatrix"
>
> deparse.level 1 2 3 4
> c 2 2 2 2
> a++ 10 10 10 10
> . 1 . .
> > rbind(1:4, c = 2, "a++" = 10, dd, deparse.level = 2) # 4 rownames
> 4 x 4 sparse Matrix of class "dgCMatrix"
>
> deparse.level 1 2 3 4
> c 2 2 2 2
> a++ 10 10 10 10
> . 1 . .
> >
>
>
>
> > On Mon, Jan 26, 2015 at 3:55 AM, Martin Maechler <
> > maechler at lynne.stat.math.ethz.ch> wrote:
>
> >> >>>>> Michael Lawrence <lawrence.michael at gene.com> >>>>>
> >> on Sat, 24 Jan 2015 06:39:37 -0800 writes:
> >>
> >> > On Sat, Jan 24, 2015 at 12:58 AM, Mario Annau >
> >> <mario.annau at gmail.com> wrote: >> Hi all, this question
> >> has already been posted on >> stackoverflow, however
> >> without success, see also
> >> >>
> >> http://stackoverflow.com/questions/27886535/proper-way-to-use-cbind-rbind-with-s4-classes-in-package
> >> .
> >> >>
> >> >> I have written a package using S4 classes and would
> >> like >> to use the functions rbind, cbind with these
> >> defined >> classes.
> >> >>
> >> >> Since it does not seem to be possible to define rbind
> >> and >> cbind directly as S4 methods (see ?cBind) I
> >> defined >> rbind2 and cbind2 instead:
> >> >>
> >>
> >> > This needs some clarification. It certainly is possible
> >> to > define cbind and rbind methods. The BiocGenerics
> >> package > defines generics for those and many methods are
> >> defined by > e.g. S4Vectors, IRanges, etc. The issue is
> >> that dispatch > on "..." is singular, i.e., you can only
> >> specify one class > that all args in "..." must share
> >> (potentially through > inheritance).
> >>
> >> > Thus, trying to combine objects from a > different
> >> hierarchy (or non-S4 objects) will not > work.
> >>
> >> Yes, indeed, that's the drawback
> >>
> >> I've been there almost surely before everyone else, with
> >> the Matrix package... and I have been the author of
> >> cbind2(), rbind2(), and of course, of cBind(), and
> >> rBind().
> >>
> >> At the time when I introduced these, the above
> >> possibility of writing S4 methods for '...' where not
> >> yet part of R.
> >>
> >> > This has not been a huge problem for us in >
> >> practice. For example, we have a DataFrame object that >
> >> mimics data.frame. To cbind a data.frame with a
> >> DataFrame, > the user can just call the DataFrame() >
> >> constructor. rbind() between different data structures is
> >> > much less common.
> >>
> >> well... yes and no. Think of using the Matrix package,
> >> maybe with another package that defines another
> >> generalized matrix class... It would be nice if things
> >> worked automatically / perfectly there.
> >>
> >> > The cBind and rBind functions in Matrix (and the
> >> r/cbind > that get installed by bind_activation, the code
> >> is shared) > work by recursing, dropping the first
> >> argument until two > are left, and then combining with
> >> r/cbind2(). The Biobase > package uses a similar strategy
> >> to mimic c() via its > non-standard combine()
> >> generic. The nice thing about the > combine() approach is
> >> the user entry point and the generic > are the same,
> >> instead of having methods on rbind2() and > the user
> >> calling rBind().
> >>
> >> > I would argue that bind_activation(TRUE) should be >
> >> discouraged,
> >>
> >> Yes, you are right Michael; it should be discouraged at
> >> least to be run in a *package*. One could think of its
> >> use by an explicit user call.
> >>
> >> > because it replaces the native rbind and > cbind with
> >> recursive variants that are going to cause > problems,
> >> performance and otherwise. This is why it is >
> >> hidden. Perhaps a reasonable compromise would be for the
> >> > native cbind and rbind to check whether any arguments
> >> are > S4 and if so, resort to recursion. Recursion does
> >> seem to > be a clean way to implement "type promotion",
> >> i.e., to > answer the question "which type should the
> >> result be when > faced with mixed-type args?".
> >>
> >> Exactly. That has been my idea at the time .. ((yes,
> >> I'm also the author of the bind_activation()
> >> "(mis)functionality".))
> >>
> >> > Hopefully others have better ideas.
> >>
> >> that would be great.
> >>
> >> And even if not, it would be great if we could implement
> >> your idea > Perhaps a reasonable compromise would be for
> >> the > native cbind and rbind to check whether any
> >> arguments are > S4 and if so, resort to recursion.
> >>
> >> without a noticable performance penalty in the case of no
> >> S4 arguments.
> >>
> >> Martin
> >>
> >>
> >> > Michael
> >>
> >> >> setMethod("rbind2", signature(x="ClassA", y = "ANY"),
> >> >> function(x, y) { # Do stuff ... })
> >> >>
> >> >> setMethod("cbind2", signature(x="ClassA", y = "ANY"),
> >> >> function(x, y) { # Do stuff ... })
> >> >>
> >> >> >From ?cbind2 I learned that these functions need to
> >> be >> activated using methods:::bind_activation to
> >> replace >> rbind and cbind from base.
> >> >>
> >> >> I included the call in the package file R/zzz.R using
> >> the >> .onLoad function:
> >> >>
> >> >> .onLoad <- function(...) { # Bind activation of
> >> cbind(2) >> and rbind(2) for S4 classes >>
> >> methods:::bind_activation(TRUE) } This works as >>
> >> expected. However, running R CMD check I am now getting
> >> >> the following NOTE since I am using an unexported >>
> >> function in methods:
> >> >>
> >> >> * checking dependencies in R code ... NOTE Unexported
> >> >> object imported by a ':::' call: >>
> >> 'methods:::bind_activation' See the note in ?`:::` about
> >> >> the use of this operator. How can I get rid of the
> >> NOTE >> and what is the proper way to define the methods
> >> cbind >> and rbind for S4 classes in a package?
> >> >>
> >> >> Best, mario
> >> >>
> >> >> ______________________________________________ >>
> >> R-devel at r-project.org mailing list >>
> >> https://stat.ethz.ch/mailman/listinfo/r-devel
> >>
> >> > ______________________________________________ >
> >> R-devel at r-project.org mailing list >
> >> https://stat.ethz.ch/mailman/listinfo/r-devel
> >>
>
More information about the R-devel
mailing list