From marc_schwartz at me.com  Wed Dec  1 14:59:00 2010
From: marc_schwartz at me.com (Marc Schwartz)
Date: Wed, 01 Dec 2010 07:59:00 -0600
Subject: [Rd] RFC: sapply() limitation from vector to matrix,
 but not further
In-Reply-To: <19702.2469.739772.13707@lynne.math.ethz.ch>
References: <19702.2469.739772.13707@lynne.math.ethz.ch>
Message-ID: <D5DCB29D-46D5-458C-B315-472699A02AAB@me.com>

On Dec 1, 2010, at 2:39 AM, Martin Maechler wrote:

> sapply() stems from S / S+ times and hence has a long tradition.
> In spite of that I think that it should be enhanced...
> 
> As the subject mentions, sapply() produces a matrix in cases
> where the list components of the lapply(.) results are of the
> same length (and ...).
> However, it unfortunately "stops there".
> E.g., if you *nest* two sapply() calls where the inner one
> produces a matrix, very often the logical behavior would be for
> the outer sapply() to stack these matrices into an array of 
> rank 3 ["array rank"(x) := length(dim(x))].
> However it does not do that, e.g., an artifical example
> 
> p0 <- function(...) paste(..., sep="")
> myF <- function(x,y) {
>    stopifnot(length(x) <= 3)
>    x <- rep(x, length.out=3)
>    ny <- length(y)
>    r <- outer(x,y)
>    dimnames(r) <- list(p0("r",1:3), p0("C", seq_len(ny)))
>    r
> }
> 
> and
> 
>> (v <- structure(10*(5:8), names=LETTERS[1:4]))
> A  B  C  D 
> 50 60 70 80 
> 
> if we let sapply() not simplify, we see the list of same size
> matrices it produes:
> 
>> sapply(v, myF, y = 2*(1:5), simplify=FALSE)
> $A
>    C1  C2  C3  C4  C5
> r1 100 200 300 400 500
> r2 100 200 300 400 500
> r3 100 200 300 400 500
> 
> $B
>    C1  C2  C3  C4  C5
> r1 120 240 360 480 600
> r2 120 240 360 480 600
> r3 120 240 360 480 600
> 
> $C
>    C1  C2  C3  C4  C5
> r1 140 280 420 560 700
> r2 140 280 420 560 700
> r3 140 280 420 560 700
> 
> $D
>    C1  C2  C3  C4  C5
> r1 160 320 480 640 800
> r2 160 320 480 640 800
> r3 160 320 480 640 800
> 
> However, quite deceptively
> 
>> sapply(v, myF, y = 2*(1:5))
>        A   B   C   D
> [1,] 100 120 140 160
> [2,] 100 120 140 160
> [3,] 100 120 140 160
> [4,] 200 240 280 320
> [5,] 200 240 280 320
> [6,] 200 240 280 320
> [7,] 300 360 420 480
> [8,] 300 360 420 480
> [9,] 300 360 420 480
> [10,] 400 480 560 640
> [11,] 400 480 560 640
> [12,] 400 480 560 640
> [13,] 500 600 700 800
> [14,] 500 600 700 800
> [15,] 500 600 700 800
> 
> 
> My proposal -- implemented and "make check" tested --
> is to add an optional argument  'ARRAY'
> which allows
> 
>> sapply(v, myF, y = 2*(1:5), ARRAY=TRUE)
> , , A
> 
>    C1  C2  C3  C4  C5
> r1 100 200 300 400 500
> r2 100 200 300 400 500
> r3 100 200 300 400 500
> 
> , , B
> 
>    C1  C2  C3  C4  C5
> r1 120 240 360 480 600
> r2 120 240 360 480 600
> r3 120 240 360 480 600
> 
> , , C
> 
>    C1  C2  C3  C4  C5
> r1 140 280 420 560 700
> r2 140 280 420 560 700
> r3 140 280 420 560 700
> 
> , , D
> 
>    C1  C2  C3  C4  C5
> r1 160 320 480 640 800
> r2 160 320 480 640 800
> r3 160 320 480 640 800
> 
>> 
> -----------
> 
> In the best of all worlds, the default would be 'ARRAY = TRUE',
> but of course, given the long-standing different behavior,
> it seem much too "risky", and my proposal includes remaining
> back-compatible with default ARRAY = FALSE.
> 
> Martin Maechler,
> ETH Zurich


Seems to me to be a reasonable proposal Martin, obviously with the proviso that the current default behavior is unaltered, as you note.

Regards,

Marc



[Rd] RFC: sapply() limitation from vector to matrix, but not further
Marc Schwartz