Как обобщить внешние на n измерений? - PullRequest
8 голосов
/ 31 мая 2011

Стандартное выражение R outer(X, Y, f) соответствует матрице, чья (i, j) -я запись имеет значение f(X[i], Y[j]).

Я хотел бы реализовать функцию multi.outer, n-мерное обобщение outer: multi.outer(f, X_1, ..., X_n), где f - некоторая n-арная функция, выдает (length (X_1) * ... * length (X_n)) массив, чья (i_1, ..., i_n) -я запись имеет значение f(X_1[i_1], ..., X_n[i_n]) для всех допустимых наборов индексов (i_1, ..., i_n). Ясно, что для каждого i в {1, ..., n} все элементы X_i в multi.outer(f, X_1,...,X_i,..., X_n) должны быть допустимыми i-тыми аргументами для функции f. Для случая n = 2 multi.outer будет делать то же самое, что и outer, хотя у него будет другая подпись (IOW, multi.outer(f, X, Y) будет эквивалентно outer(X, Y, f)).

Важно отметить, что, хотя аргументы X_1, ..., X_n multi.outer являются векторами, они не обязательно имеют одинаковый режим. Например. X_1 и X_2 могут быть c(1, 2, 3) и LETTERS[10:20] соответственно.

Спасибо!

Ответы [ 3 ]

16 голосов
/ 01 июня 2011

Это один из способов: сначала используйте Vectorize и outer, чтобы определить функцию, которая создает n-мерную матрицу, где каждая запись представляет собой список аргументов, к которым будет применена данная функция:

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)
}

Теперь multi.outer просто нужно вызвать apply и do.call для этой "args-matrix":

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

Давайте попробуем это с примером функции:

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"
1 голос
/ 01 июня 2011

Как насчет этого:


multi.outer<-function(f,...){

  apply(expand.grid(...),1,function(x){do.call(f,as.list(x))})

}
0 голосов
/ 11 августа 2017

Я думаю, что мы можем сделать это, используя Outer и 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)

Теперь sigm1 (x = 1: 3) дает требуемый вывод

 [[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

Единственный недостаток этого фрагмента кода - использование значений по умолчанию a =-1: 1 и b = -1: 1.Когда я пытаюсь передать то же самое во время вызова функции, он становится бесполезным.Например,

sigm1(-1:1,-1:1,1:3)

[[1]]
      [,1]
[1,] 0.1353353

[[2]]
 [,1]
[1,]    1

[[3]]
     [,1]
[1,] 54.59815

Я не могу понять, почему передача аргументов приводит к такой разнице в выводе.

...