Michael Lawrence
2015-Feb-09 22:38 UTC
[Rd] Proper way to define cbind, rbind for s4 classes in package
Are you able to create a reproducible example, somehow? Thanks, Michael On Mon, Feb 9, 2015 at 2:28 PM, Mario Annau <mario.annau at gmail.com> wrote:> 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 > > >> > > >[[alternative HTML version deleted]]
Mario Annau
2015-Feb-11 19:18 UTC
[Rd] Proper way to define cbind, rbind for s4 classes in package
sorry - I just got irritated by my different R-versions. The behaviour I described in the previous mail was discovered using R 3.1.2 without bind_activation(TRUE). In r67773 all calls are delegated to r/cbind.matrix and not r/cbind2. As a workaround I have now implemented an S3 method for my S4 class which correctly dispatches for both versions (3.1.2 and r67699+) - see also the commit for the h5 package on github: https://github.com/mannau/h5/commit/20daea37ade1a317458c8a1d03928f579e457f93. Any better ideas are welcome. br, mario Am 09/02/15 um 23:38 schrieb Michael Lawrence:> Are you able to create a reproducible example, somehow? > > Thanks, > Michael > > On Mon, Feb 9, 2015 at 2:28 PM, Mario Annau <mario.annau at gmail.com > <mailto:mario.annau at gmail.com>> wrote: > > 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 > <mailto: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 > <mailto:maechler at lynne.stat.math.ethz.ch>> wrote: > > > > >> >>>>> Michael Lawrence <lawrence.michael at gene.com > <mailto: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 <mailto: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 <mailto:R-devel at r-project.org> > mailing list >> > > >> https://stat.ethz.ch/mailman/listinfo/r-devel > > >> > > >> > ______________________________________________ > > > >> R-devel at r-project.org <mailto:R-devel at r-project.org> > mailing list > > > >> https://stat.ethz.ch/mailman/listinfo/r-devel > > >> > > > >
Martin Maechler
2015-Feb-20 11:40 UTC
[Rd] Proper way to define cbind, rbind for s4 classes in package
>>>>> Mario Annau <mario.annau at gmail.com> >>>>> on Wed, 11 Feb 2015 20:18:53 +0100 writes:> sorry - I just got irritated by my different R-versions. > The behaviour I described in the previous mail was discovered using R > 3.1.2 without bind_activation(TRUE). In r67773 all calls are delegated > to r/cbind.matrix and not r/cbind2. > As a workaround I have now implemented an S3 method for my S4 class > which correctly dispatches for both versions (3.1.2 and r67699+) - see > also the commit for the h5 package on github: > https://github.com/mannau/h5/commit/20daea37ade1a317458c8a1d03928f579e457f93. > Any better ideas are welcome. and in the mean time there have been a few off-list e-mails, {"No, using an S3 method was definitely not the idea of Michael's changes!" .. } and many hours of work by me. R-devel svn rev 67852 and later now has cbind() / rbind() working in a better way, dipatching to either cbind2(), rbind2() S4 methods for "your" classes, or to S4 rbind() or cbind() methods for your classes. Notably the new code now should create column / rownames analogously to base::cbind / rbind, influenced by deparse.level in the case of non-matrix arguments. Small changes in some outputs may occur, notably as the hidden methods:::cbind and rbind functions (think of "S4 default method") now do obey deparse.level and also otherwise should create row and column names in the same way as base::[cr]bind(). Martin Maechler ETH Zurich and R Core Team > br, > mario > Am 09/02/15 um 23:38 schrieb Michael Lawrence: >> Are you able to create a reproducible example, somehow? >> >> Thanks, >> Michael >> >> On Mon, Feb 9, 2015 at 2:28 PM, Mario Annau <mario.annau at gmail.com >> <mailto:mario.annau at gmail.com>> wrote: >> >> 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 >> <mailto: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 >> <mailto:maechler at lynne.stat.math.ethz.ch>> wrote: >> > >> > >> >>>>> Michael Lawrence <lawrence.michael at gene.com >> <mailto: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 <mailto: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 <mailto:R-devel at r-project.org> >> mailing list >> >> > >> https://stat.ethz.ch/mailman/listinfo/r-devel >> > >> >> > >> > ______________________________________________ > >> > >> R-devel at r-project.org <mailto:R-devel at r-project.org> >> mailing list > >> > >> https://stat.ethz.ch/mailman/listinfo/r-devel >> > >> >> > >> >>
Possibly Parallel Threads
- Proper way to define cbind, rbind for s4 classes in package
- Proper way to define cbind, rbind for s4 classes in package
- Proper way to define cbind, rbind for s4 classes in package
- Proper way to define cbind, rbind for s4 classes in package
- Proper way to define cbind, rbind for s4 classes in package