How to generalize external and n dimensions?

The standard expression R outer(X, Y, f) is estimated by a matrix whose (i, j) th record has the value f(X[i], Y[j]) .

I would like to implement a multi.outer function, an n-dimensional generalization of outer : multi.outer(f, X_1, ..., X_n) , where f is some n-ary function, will create (length (X_1) * ... * length (X_n)) whose record (i_1, ..., i_n) -th has the value f(X_1[i_1], ..., X_n[i_n]) for all valid index sets (i_1, ..., i_n ) It is clear that for every i in {1, ..., n} all the elements X_i in multi.outer(f, X_1,...,X_i,..., X_n) must be valid ith arguments for the function f . For the case n = 2, multi.outer will do the same as outer , although it will have a different signature (IOW, multi.outer(f, X, Y) will be equivalent to outer(X, Y, f) ).

It is important to note that although the arguments X_1, ..., X_n of multi.outer are all vectors, they do not necessarily have the same mode. For example. X_1 and X_2 can be c(1, 2, 3) and LETTERS[10:20] respectively.

Thanks!

+8
outer-join vectorization r reduce
source share
3 answers

This is one way: first use Vectorize and outer to define a function that creates an n-dimensional matrix, where each record is a list of arguments that this function will be applied to:

 list_args <- Vectorize( function(a,b) c( as.list(a), as.list(b) ), SIMPLIFY = FALSE) make_args_mtx <- function( alist ) { Reduce(function(x, y) outer(x, y, list_args), alist) } 

Now multi.outer just needs to call apply and do.call in this "args-matrix":

 multi.outer <- function(f, ... ) { args <- make_args_mtx(list(...)) apply(args, 1:length(dim(args)), function(a) do.call(f, a[[1]] ) ) } 

Try this with an example function:

 fun <- function(a,b,c) paste(a,b,c) ans <- multi.outer(fun, LETTERS[1:2], c(3, 4, 5), letters[6:7] ) > ans , , 1 [,1] [,2] [,3] [1,] "A 3 f" "A 4 f" "A 5 f" [2,] "B 3 f" "B 4 f" "B 5 f" , , 2 [,1] [,2] [,3] [1,] "A 3 g" "A 4 g" "A 5 g" [2,] "B 3 g" "B 4 g" "B 5 g" 
+16
source share

How about this:

 multi.outer<-function(f,...){ apply(expand.grid(...),1,function(x){do.call(f,as.list(x))}) } 
+1
source share

I think we can do this using Outer and Vectorize.

  sigm = function(a=0,b=0,x){ return(exp(x*a+b)) } sigm1 = Vectorize(function(a=-1:1,b=-1:1,x){ outer(a,b,sigm,x) },SIMPLIFY = FALSE) 

Now sigm1 (x = 1: 3) gives the required output

  [[1]] [,1] [,2] [,3] [1,] 0.1353353 0.3678794 1.000000 [2,] 0.3678794 1.0000000 2.718282 [3,] 1.0000000 2.7182818 7.389056 [[2]] [,1] [,2] [,3] [1,] 0.04978707 0.1353353 0.3678794 [2,] 0.36787944 1.0000000 2.7182818 [3,] 2.71828183 7.3890561 20.0855369 [[3]] [,1] [,2] [,3] [1,] 0.01831564 0.04978707 0.1353353 [2,] 0.36787944 1.00000000 2.7182818 [3,] 7.38905610 20.08553692 54.5981500 

The only rollback using this piece of code is that I use the default values ​​a = -1: 1 and b = -1: 1. When I try to pass the same thing during a function call, it goes haywire. For example.

 sigm1(-1:1,-1:1,1:3) [[1]] [,1] [1,] 0.1353353 [[2]] [,1] [1,] 1 [[3]] [,1] [1,] 54.59815 

I cannot understand why passing arguments makes this difference in output.

0
source share

All Articles