kjo
kjo

Reputation: 35331

Generating an n-dimensional array by applying function to all pairs

Let F be a function that takes two arguments and returns a numeric k-vector (i.e. a numeric vector of length k) for some integer k > 1, and let X and Y be vectors of values suitable as first and second arguments (respectively) of F.

Is there a "functional" (i.e. loop-free), and convenient, way to generate the 3-dimensional array A such that, for all suitable i and j, the following expression is TRUE?

all.equal(A[i, j, ], F(X[[i]], Y[[j]]))

More generally, let F be a function that takes two arguments and returns a numeric n-dimensional array (where, dim(F(x, y)) = D is a constant n-vector, for all valid arguments x and y), and, as before, let X and Y be vectors of values suitable as first and second arguments (respectively) of F.

Is there a "functional" (i.e. loop-free), and convenient, way to generate the (n + 2)-dimensional array A (with dim(A) equal to c(length(X), length(Y), D)) such that, for all suitable i and j, the following expression is TRUE?

all.equal(A[i, j, , ... , ], F(X[[i]], Y[[j]]))

AFAICT, outer won't work for this. For example, suppose that F is defined like this:

F <- function (m, n) {
    set.seed(m - n);
    array(runif(6), dim = c(3, 2))
}

This F takes two integers as arguments, and returns a 3 × 2 array of floating-point numbers. E.g.:

F(3, 8)
          [,1]      [,2]
[1,] 0.7345948 0.6368477
[2,] 0.3396928 0.9620946
[3,] 0.1910453 0.3756567

But

outer(1:3, 1:3, Vectorize(F))
Error in outer(1:3, 1:3, Vectorize(F)) : 
  dims [product 9] do not match the length of object [54]

I figure the reason outer does not work here is because (emphasis added)

outer(X, Y, FUN = "*", ...)

...

‘FUN’ is called with these two extended vectors as arguments (plus any arguments in ‘...’). It must be a vectorized function (or the name of one) expecting at least two arguments and returning a value with the same length as the first (and the second).


Update for Vlo:

## the line below is cut-and-paste'd directly from comment
F<-function(m,n){set.seed(m - n);list(array(runif(6), dim = c(3, 2)))}; x = outer(1:3, 1:5, Vectorize(F, "m", "n"))

If I now inspect x, I get: x

     [,1]      [,2]      [,3]      [,4]      [,5]     
[1,] Numeric,6 Numeric,6 Numeric,6 Numeric,6 Numeric,6
[2,] Numeric,6 Numeric,6 Numeric,6 Numeric,6 Numeric,6
[3,] Numeric,6 Numeric,6 Numeric,6 Numeric,6 Numeric,6

This is not what I'm looking for. For one thing, dim(x) here equals the vector c(3, 5), whereas for the desired x it should be that

dim(x)
[1] 3 5 3 2

and

for (i in 1:3) {
    for (j in 1:5) {
        cat('\n', paste(c(i, j, '', ''), collapse = ', '), '\n')
        print(x[i, j, ,])
    }
}
 1, 1, ,  
          [,1]      [,2]
[1,] 0.8966972 0.5728534
[2,] 0.2655087 0.9082078
[3,] 0.3721239 0.2016819

 1, 2, ,  
          [,1]      [,2]
[1,] 0.4866672 0.1467027
[2,] 0.1913653 0.2415895
[3,] 0.9932719 0.5371012

 1, 3, ,  
          [,1]      [,2]
[1,] 0.8141134 0.2416691
[2,] 0.5890751 0.9928682
[3,] 0.1339374 0.9844079

 1, 4, ,  
          [,1]      [,2]
[1,] 0.7705233 0.3878055
[2,] 0.7820377 0.1221871
[3,] 0.3802074 0.3378332

 1, 5, ,  
           [,1]      [,2]
[1,] 0.38668404 0.7800617
[2,] 0.09064471 0.4887107
[3,] 0.65027437 0.9742584

 2, 1, ,  
          [,1]      [,2]
[1,] 0.2655087 0.9082078
[2,] 0.3721239 0.2016819
[3,] 0.5728534 0.8983897

 2, 2, ,  
          [,1]      [,2]
[1,] 0.8966972 0.5728534
[2,] 0.2655087 0.9082078
[3,] 0.3721239 0.2016819

 2, 3, ,  
          [,1]      [,2]
[1,] 0.4866672 0.1467027
[2,] 0.1913653 0.2415895
[3,] 0.9932719 0.5371012

 2, 4, ,  
          [,1]      [,2]
[1,] 0.8141134 0.2416691
[2,] 0.5890751 0.9928682
[3,] 0.1339374 0.9844079

 2, 5, ,  
          [,1]      [,2]
[1,] 0.7705233 0.3878055
[2,] 0.7820377 0.1221871
[3,] 0.3802074 0.3378332

 3, 1, ,  
          [,1]      [,2]
[1,] 0.1848823 0.1680519
[2,] 0.7023740 0.9438393
[3,] 0.5733263 0.9434750

 3, 2, ,  
          [,1]      [,2]
[1,] 0.2655087 0.9082078
[2,] 0.3721239 0.2016819
[3,] 0.5728534 0.8983897

 3, 3, ,  
          [,1]      [,2]
[1,] 0.8966972 0.5728534
[2,] 0.2655087 0.9082078
[3,] 0.3721239 0.2016819

 3, 4, ,  
          [,1]      [,2]
[1,] 0.4866672 0.1467027
[2,] 0.1913653 0.2415895
[3,] 0.9932719 0.5371012

 3, 5, ,  
          [,1]      [,2]
[1,] 0.8141134 0.2416691
[2,] 0.5890751 0.9928682
[3,] 0.1339374 0.9844079

Upvotes: 1

Views: 59

Answers (1)

kjo
kjo

Reputation: 35331

OK, here's one clumsy solution. I post it in the hopes that it will elicit a better one from someone with a better grasp of R than I have.

outer_nd <- function(x, y, f) {
    pairs <- unname(as.matrix(expand.grid(x, y)))
    n <- nrow(pairs)
    rows <- split(pairs, seq(n))
    values <- sapply(rows, function (p) do.call(f, as.list(p)))
    array(t(values), dim = c(length(x), length(y), dim(F(x[[1]], y[[1]]))))
}

This solution at least shows explicitly what I'm after.

For example, if F is defined like this

F <- function (m, n) {
    set.seed(m - n);
    array(runif(6), dim = c(3, 2))
}

...then

z <- outer_nd(1:2, 1:2, F)
dim(z)
 [1] 2 2 3 2

and

for (i in 1:2) {
    for (j in 1:2) {
        cat('\n', paste(c(i, j, '', ''), collapse = ', '), '\n')
        print(z[i, j, ,])

        cat('\nF(', paste(c(i, j), collapse = ', '), ') =\n')
        print(F(i, j))
        cat('\n')
    }
}
 1, 1, ,  
          [,1]      [,2]
[1,] 0.8966972 0.5728534
[2,] 0.2655087 0.9082078
[3,] 0.3721239 0.2016819

F( 1, 1 ) =
          [,1]      [,2]
[1,] 0.8966972 0.5728534
[2,] 0.2655087 0.9082078
[3,] 0.3721239 0.2016819


 1, 2, ,  
          [,1]      [,2]
[1,] 0.4866672 0.1467027
[2,] 0.1913653 0.2415895
[3,] 0.9932719 0.5371012

F( 1, 2 ) =
          [,1]      [,2]
[1,] 0.4866672 0.1467027
[2,] 0.1913653 0.2415895
[3,] 0.9932719 0.5371012


 2, 1, ,  
          [,1]      [,2]
[1,] 0.2655087 0.9082078
[2,] 0.3721239 0.2016819
[3,] 0.5728534 0.8983897

F( 2, 1 ) =
          [,1]      [,2]
[1,] 0.2655087 0.9082078
[2,] 0.3721239 0.2016819
[3,] 0.5728534 0.8983897


 2, 2, ,  
          [,1]      [,2]
[1,] 0.8966972 0.5728534
[2,] 0.2655087 0.9082078
[3,] 0.3721239 0.2016819

F( 2, 2 ) =
          [,1]      [,2]
[1,] 0.8966972 0.5728534
[2,] 0.2655087 0.9082078
[3,] 0.3721239 0.2016819

Upvotes: 1

Related Questions