[R] avoiding loops, gaining generality

Bill Venables William.Venables at cmis.CSIRO.AU
Wed Sep 29 09:19:45 CEST 1999


Matthew Wiener asks:

> Hi, all.
> 
> Given an array, I'd like to add elements whose location vectors are
> permutations of one another.  For example, in a 3-dimensional array, I'd
> add the elements in positions (1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2)
> and (3,2,1); those in positions (1,2,4), (1,4,2), (2,1,4), (2,4,1),
> (4,1,2), and (4,2,1); and so on.  Elements with repeated positions
> -- (1,1,2) or (1,4,4), for example -- can be ignored.
> 
> In two dimensions, what I want can be done with (x + t(x))[lower.tri(x)]. 

One idea is to use a matrix index, of course.  The following is
not optimal by any means, but if you don't push it too far it
should work well enough.  First a couple of helper functions
whose purpose is self-evident (and the first now pretty
well-known):

subsets <- function(r, n, v = 1:n)
  if(r <= 0) NULL else
if(r >= n) v[1:n] else
rbind(cbind(v[1], Recall(r - 1, n - 1, v[-1])),
      Recall(r, n - 1, v[-1]))

permutations <- function(n, v = 1:n) {
  if(n == 1)
    return(v[1])
  X <- NULL
  for(i in 1:n)
    X <- rbind(X,
               cbind(v[i], permutations(n - 1, v[-i])))
  X
}

The following example uses a 3-dimensional array but the
generalization to any number of dimensions is no more
complicated.  It does assume that you are dealing with an array
with all dimensions having the same ranges.  If this is not the
case you will need to do some additional pruning, but that too
can be vectorized.

> A <- array(1:(4^3), dim = c(4,4,4))  # let's say

First generate the non-repeated index vectors as the rows of a
matrix:

> ind <- subsets(3, 4)
> ind
     [,1] [,2] [,3] 
[1,]    1    2    3
[2,]    1    2    4
[3,]    1    3    4
[4,]    2    3    4

Now generate the sums (this next step is the nub):

> sums <- apply(ind, 1, function(x) sum(A[permutations(3, x)]))

Finally give the vector names that identify which sum it is:

> names(sums) <- do.call("paste", c(data.frame(ind), sep = ","))

and here is what we have:

> sums
 1,2,3 1,2,4 1,3,4 2,3,4 
   132   174   216   258

Disclaimer: this was actually done in S, but you should never know!

> I'll summarize to the list.

Yes, please do, but I thought I would post this one anyway as my
puzzle of the month...

Bill Venables.

-- 
-----------------------------------------------------------------
Bill Venables, Statistician, CMIS Environmetrics Project.

Physical address:                            Postal address:
CSIRO Marine Laboratories,                   PO Box 120,       
233 Middle St, Cleveland, Queensland         Cleveland, Qld, 4163
AUSTRALIA                                    AUSTRALIA

Telephone: +61 7 3826 7251     Email: Bill.Venables at cmis.csiro.au     
      Fax: +61 7 3826 7304
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
r-help mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
Send "info", "help", or "[un]subscribe"
(in the "body", not the subject !)  To: r-help-request at stat.math.ethz.ch
_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._



More information about the R-help mailing list