Reputation: 35331
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
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