Skip to content
Prev 38749 / 63424 Next

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.array    levelplot.array*    unique.array