From friendly at yorku.ca  Mon Dec 27 17:32:26 2010
From: friendly at yorku.ca (Michael Friendly)
Date: Mon, 27 Dec 2010 11:32:26 -0500
Subject: [Rd] aperm() should retain class of input object
Message-ID: <4D18BF9A.6040404@yorku.ca>

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


-- 
Michael Friendly     Email: friendly AT yorku DOT ca
Professor, Psychology Dept.
York University      Voice: 416 736-5115 x66249 Fax: 416 736-5814
4700 Keele Street    Web:http://www.datavis.ca
Toronto, ONT  M3J 1P3 CANADA



[Rd] aperm() should retain class of input object
Michael Friendly