Re: [Rd] aperm() should retain class of input object
On 12/28/2010 9:43 AM, Tim Hesterberg wrote: Having aperm() return an object of the same class is dangerous, there are undoubtedly classes for which that is not appropriate, producing an illegal object for that class or quietly giving incorrect results. OK. I can see that my initial proposal would be dangerous for xtabs objects without further modifications and that it is unwise to change default behavior in base functions without very strong reasons. Three alternatives are to: * add the keep.class option but with default FALSE This first option is the minimally invasive corrective surgery. This would put the burden on the user (or package writer), but at least make it known that keep.class=TRUE is an option. This version is ## add keep.class, non-generic aperm - function (a, perm, resize = TRUE, keep.class=FALSE) { if (missing(perm)) perm - integer(0L) result - .Internal(aperm(a, perm, resize)) if(keep.class) class(result) - class(a) result } * make aperm a generic function - without a keep.class argument - with a ... argument - methods for classes like table could have keep.class = TRUE This would be much better, as long as an aperm.table method was added to base, to complement table() itself, and gives the desired behavior for table objects by default. This version seems to be: ## make generic, with ... aperm - function(a, ...) UseMethod(aperm, ...) aperm.default - function (a, perm, resize = TRUE, ...) { if (missing(perm)) perm - integer(0L) .Internal(aperm(a, perm, resize)) } aperm.table - function(a, perm, resize=TRUE, keep.class=TRUE, ...) { result - aperm.default(a, perm, resize=resize) if(keep.class) class(result) - class(a) result } But it throws an error, maybe because I haven't redefined aperm as a generic: UCB - aperm(UCBAdmissions, c(2,1,3)) Error in aperm(UCBAdmissions, c(2, 1, 3)) : '...' used in an incorrect context The .table method does work as desired: UCB - aperm.table(UCBAdmissions, c(2,1,3)) str(UCB) table [1:2, 1:2, 1:6] 512 89 313 19 353 17 207 8 120 202 ... - attr(*, dimnames)=List of 3 ..$ Gender: chr [1:2] Male Female ..$ Admit : chr [1:2] Admitted Rejected ..$ Dept : chr [1:6] A B C D ... * make aperm a generic function - without a keep.class argument - with a ... argument - default method have keep.class = TRUE The third option would give the proposed behavior by default, but allow a way out for classes where the behavior is wrong. This puts the burden on a class author to realize the potential problem with aperm, so my preference is one of the first two options. aperm() was designed for multidimensional arrays, but is also useful for table objects, particularly with the lattice, vcd and vcdExtra packages. But aperm() was designed and implemented before other related object classes were conceived, and I propose a small tune-up to make it more generally useful. The problem is that aperm() always returns an object of class 'array', which causes problems for methods designed for table objects. It also requires some package writers to implement both .array and .table methods for the same functionality, usually one in terms of the other. Some examples of unexpected, and initially perplexing results (when only methods for one class are implemented) are shown below. library(vcd) pairs(UCBAdmissions, shade=TRUE) UCB- aperm(UCBAdmissions, c(2, 1, 3)) # UCB is now an array, not a table pairs(UCB, shade=TRUE) There were 50 or more warnings (use warnings() to see the first 50) # fix it, to get pairs.table class(UCB)- table pairs(UCB, shade=TRUE) Of course, I can define a new function, tperm() that does what I think should be the expected behavior: # aperm, for table objects tperm- function(a, perm, resize = TRUE) { result- aperm(a, per, resize) class(result)- class(a) result } But I think it is more natural to include this functionality in aperm() itself. Thus, I propose the following revision of base::aperm(), at the R level: aperm- function (a, perm, resize = TRUE, keep.class=TRUE) { if (missing(perm)) perm- integer(0L) result- .Internal(aperm(a, perm, resize)) if(keep.class) class(result)- class(a) result } I don't think this would break any existing code, except where someone depended on coercion to an array. The drop-in replacement for aperm would set keep.class=FALSE by default, but I think TRUE is more natural. FWIW, here are the methods for table and array objects from my current (non-representative) session. methods(class=table) [1] as.data.frame.table barchart.table* cloud.table* contourplot.table* dotplot.table* [6] head.table* levelplot.table*pairs.table* plot.table* print.table [11] summary.table tail.table* Non-visible functions are asterisked methods(class=array) [1] anyDuplicated.array as.data.frame.array as.raster.array* barchart.array*
Re: [Rd] aperm() should retain class of input object
Michael Friendly frien...@yorku.ca on Wed, 29 Dec 2010 08:33:07 -0500 writes: On 12/28/2010 9:43 AM, Tim Hesterberg wrote: Having aperm() return an object of the same class is dangerous, there are undoubtedly classes for which that is not appropriate, producing an illegal object for that class or quietly giving incorrect results. OK. I can see that my initial proposal would be dangerous for xtabs objects without further modifications and that it is unwise to change default behavior in base functions without very strong reasons. Three alternatives are to: * add the keep.class option but with default FALSE This first option is the minimally invasive corrective surgery. This would put the burden on the user (or package writer), but at least make it known that keep.class=TRUE is an option. This version is ## add keep.class, non-generic aperm - function (a, perm, resize = TRUE, keep.class=FALSE) { if (missing(perm)) perm - integer(0L) result - .Internal(aperm(a, perm, resize)) if(keep.class) class(result) - class(a) result } * make aperm a generic function - without a keep.class argument - with a ... argument - methods for classes like table could have keep.class = TRUE This would be much better, as long as an aperm.table method was added to base, to complement table() itself, and gives the desired behavior for table objects by default. This version seems to be: ## make generic, with ... aperm - function(a, ...) UseMethod(aperm, ...) use UseMethod(aperm) instead and then all is fine aperm.default - function (a, perm, resize = TRUE, ...) { if (missing(perm)) perm - integer(0L) .Internal(aperm(a, perm, resize)) } aperm.table - function(a, perm, resize=TRUE, keep.class=TRUE, ...) { result - aperm.default(a, perm, resize=resize) if(keep.class) class(result) - class(a) result } But it throws an error, maybe because I haven't redefined aperm as a generic: UCB - aperm(UCBAdmissions, c(2,1,3)) Error in aperm(UCBAdmissions, c(2, 1, 3)) : '...' used in an incorrect context .. well, because you've used extraneous ... in the S3 generic definition (see above). I'm really sympathetic with your proposal and would indeed implement it (for R-devel aka R 2.13.0 to be) unless someone has good arguments for something else. {{well, my version *would* keep the 'perm = NULL' default for both default and table methods.}} Martin Maechler, ETH Zurich The .table method does work as desired: UCB - aperm.table(UCBAdmissions, c(2,1,3)) str(UCB) table [1:2, 1:2, 1:6] 512 89 313 19 353 17 207 8 120 202 ... - attr(*, dimnames)=List of 3 ..$ Gender: chr [1:2] Male Female ..$ Admit : chr [1:2] Admitted Rejected ..$ Dept : chr [1:6] A B C D ... * make aperm a generic function - without a keep.class argument - with a ... argument - default method have keep.class = TRUE The third option would give the proposed behavior by default, but allow a way out for classes where the behavior is wrong. This puts the burden on a class author to realize the potential problem with aperm, so my preference is one of the first two options. aperm() was designed for multidimensional arrays, but is also useful for table objects, particularly with the lattice, vcd and vcdExtra packages. But aperm() was designed and implemented before other related object classes were conceived, and I propose a small tune-up to make it more generally useful. The problem is that aperm() always returns an object of class 'array', which causes problems for methods designed for table objects. It also requires some package writers to implement both .array and .table methods for the same functionality, usually one in terms of the other. Some examples of unexpected, and initially perplexing results (when only methods for one class are implemented) are shown below. library(vcd) pairs(UCBAdmissions, shade=TRUE) UCB- aperm(UCBAdmissions, c(2, 1, 3)) # UCB is now an array, not a table pairs(UCB, shade=TRUE) There were 50 or more warnings (use warnings() to see the first 50) # fix it, to get pairs.table class(UCB)- table pairs(UCB, shade=TRUE) Of course, I can define a new function, tperm() that does what I think should be the expected behavior: # aperm, for table objects tperm- function(a, perm, resize = TRUE) { result- aperm(a, per, resize) class(result)- class(a) result } But I think it is more natural to include this functionality in aperm() itself. Thus, I propose the
Re: [Rd] aperm() should retain class of input object
Having aperm() return an object of the same class is dangerous, there are undoubtedly classes for which that is not appropriate, producing an illegal object for that class or quietly giving incorrect results. Three alternatives are to: * add the keep.class option but with default FALSE * make aperm a generic function - without a keep.class argument - with a ... argument - methods for classes like table could have keep.class = TRUE * make aperm a generic function - without a keep.class argument - with a ... argument - default method have keep.class = TRUE The third option would give the proposed behavior by default, but allow a way out for classes where the behavior is wrong. This puts the burden on a class author to realize the potential problem with aperm, so my preference is one of the first two options. aperm() was designed for multidimensional arrays, but is also useful for table objects, particularly with the lattice, vcd and vcdExtra packages. But aperm() was designed and implemented before other related object classes were conceived, and I propose a small tune-up to make it more generally useful. The problem is that aperm() always returns an object of class 'array', which causes problems for methods designed for table objects. It also requires some package writers to implement both .array and .table methods for the same functionality, usually one in terms of the other. Some examples of unexpected, and initially perplexing results (when only methods for one class are implemented) are shown below. library(vcd) pairs(UCBAdmissions, shade=TRUE) UCB - aperm(UCBAdmissions, c(2, 1, 3)) # UCB is now an array, not a table pairs(UCB, shade=TRUE) There were 50 or more warnings (use warnings() to see the first 50) # fix it, to get pairs.table class(UCB) - table pairs(UCB, shade=TRUE) Of course, I can define a new function, tperm() that does what I think should be the expected behavior: # aperm, for table objects tperm - function(a, perm, resize = TRUE) { result - aperm(a, per, resize) class(result) - class(a) result } But I think it is more natural to include this functionality in aperm() itself. Thus, I propose the following revision of base::aperm(), at the R level: aperm - function (a, perm, resize = TRUE, keep.class=TRUE) { if (missing(perm)) perm - integer(0L) result - .Internal(aperm(a, perm, resize)) if(keep.class) class(result) - class(a) result } I don't think this would break any existing code, except where someone depended on coercion to an array. The drop-in replacement for aperm would set keep.class=FALSE by default, but I think TRUE is more natural. FWIW, here are the methods for table and array objects from my current (non-representative) session. methods(class=table) [1] as.data.frame.table barchart.table* cloud.table* contourplot.table* dotplot.table* [6] head.table* levelplot.table*pairs.table* plot.table* print.table [11] summary.table tail.table* Non-visible functions are asterisked methods(class=array) [1] anyDuplicated.array as.data.frame.array as.raster.array* barchart.array* contourplot.array* dotplot.array* [7] duplicated.arraylevelplot.array*unique.array -- Michael Friendly Email: friendly AT yorku DOT ca Professor, Psychology Dept. York University Voice: 416 736-5115 x66249 Fax: 416 736-5814 4700 Keele StreetWeb:http://www.datavis.ca Toronto, ONT M3J 1P3 CANADA __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
[Rd] aperm() should retain class of input object
aperm() was designed for multidimensional arrays, but is also useful for table objects, particularly with the lattice, vcd and vcdExtra packages. But aperm() was designed and implemented before other related object classes were conceived, and I propose a small tune-up to make it more generally useful. The problem is that aperm() always returns an object of class 'array', which causes problems for methods designed for table objects. It also requires some package writers to implement both .array and .table methods for the same functionality, usually one in terms of the other. Some examples of unexpected, and initially perplexing results (when only methods for one class are implemented) are shown below. library(vcd) pairs(UCBAdmissions, shade=TRUE) UCB - aperm(UCBAdmissions, c(2, 1, 3)) # UCB is now an array, not a table pairs(UCB, shade=TRUE) There were 50 or more warnings (use warnings() to see the first 50) # fix it, to get pairs.table class(UCB) - table pairs(UCB, shade=TRUE) Of course, I can define a new function, tperm() that does what I think should be the expected behavior: # aperm, for table objects tperm - function(a, perm, resize = TRUE) { result - aperm(a, per, resize) class(result) - class(a) result } But I think it is more natural to include this functionality in aperm() itself. Thus, I propose the following revision of base::aperm(), at the R level: aperm - function (a, perm, resize = TRUE, keep.class=TRUE) { if (missing(perm)) perm - integer(0L) result - .Internal(aperm(a, perm, resize)) if(keep.class) class(result) - class(a) result } I don't think this would break any existing code, except where someone depended on coercion to an array. The drop-in replacement for aperm would set keep.class=FALSE by default, but I think TRUE is more natural. FWIW, here are the methods for table and array objects from my current (non-representative) session. methods(class=table) [1] as.data.frame.table barchart.table* cloud.table* contourplot.table* dotplot.table* [6] head.table* levelplot.table*pairs.table* plot.table* print.table [11] summary.table tail.table* Non-visible functions are asterisked methods(class=array) [1] anyDuplicated.array as.data.frame.array as.raster.array* barchart.array* contourplot.array* dotplot.array* [7] duplicated.arraylevelplot.array*unique.array -- Michael Friendly Email: friendly AT yorku DOT ca Professor, Psychology Dept. York University Voice: 416 736-5115 x66249 Fax: 416 736-5814 4700 Keele StreetWeb:http://www.datavis.ca Toronto, ONT M3J 1P3 CANADA __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel