external (), эквивалентный для невекторных списков в R - PullRequest
7 голосов
/ 12 ноября 2009

Я понимаю, как external () работает в R:

> outer(c(1,2,4),c(8,16,32), "*")

     [,1] [,2] [,3]
[1,]    8   16   32
[2,]   16   32   64
[3,]   32   64  128

Это в основном занимает 2 вектора, находит перекрестный продукт этих векторов и затем применяет функцию к каждой паре в перекрестном продукте.

Однако у меня нет двух векторов. У меня есть два списка матриц:

M = list ();

M[[1]] = matrix(...)
M[[2]] = matrix(...)
M[[3]] = matrix(...)

И я хочу сделать операцию в моем списке матриц. Я хочу сделать:

outer(M, M, "*")

В этом случае я хочу взять скалярное произведение каждой имеющейся у меня комбинации матриц.

На самом деле я пытаюсь сгенерировать матрицу ядра (и я написал функцию ядра), поэтому я хочу сделать:

outer(M, M, kernelFunction)

где kernelFunction вычисляет расстояние между моими двумя матрицами.

Проблема в том, что external () принимает только «векторные» аргументы, а не «list» и т. Д. Есть ли функция, которая делает эквивалент external () для не векторных объектов?

С другой стороны, я мог бы использовать цикл for для этого:

M = list() # Each element in M is a matrix

for (i in 1:numElements)
{
   for (j in 1:numElements)
   {
      k = kernelFunction(M[[i]], M[[j]])
      kernelMatrix[i,j] = k;
   }
} 

но я пытаюсь избежать этого в пользу конструкции R (которая может быть более эффективной). (Да, я знаю, что могу изменить цикл for для вычисления диагональной матрицы и сэкономить 50% вычислений. Но это не тот код, который я пытаюсь оптимизировать!)

Возможно ли это? Есть мысли / предложения?

Ответы [ 3 ]

15 голосов
/ 05 апреля 2011

Внешняя функция на самом деле работает со списками, но предоставляемая вами функция повторяет два входных вектора, чтобы они содержали все возможные комбинации ...

Что быстрее, сочетание внешнего с vapply в 3 раза быстрее, чем двойной цикл for на моей машине. Если реальная функция ядра выполняет «реальную работу», разница в скорости зацикливания, вероятно, не так важна.

f1 <- function(a,b, fun) {
  outer(a, b, function(x,y) vapply(seq_along(x), function(i) fun(x[[i]], y[[i]]), numeric(1)))
}

f2 <- function(a,b, fun) {
    kernelMatrix <- matrix(0L, length(a), length(b))
    for (i in seq_along(a))
    {
       for (j in seq_along(b))
       {
          kernelMatrix[i,j] = fun(a[[i]], b[[j]])
       }
    }
    kernelMatrix
}

n <- 300
m <- 2
a <- lapply(1:n, function(x) matrix(runif(m*m),m))
b <- lapply(1:n, function(x) matrix(runif(m*m),m))
kernelFunction <- function(x,y) 0 # dummy, so we only measure the loop overhead

> system.time( r1 <- f1(a,b, kernelFunction) )
   user  system elapsed 
   0.08    0.00    0.07 
> system.time( r2 <- f2(a,b, kernelFunction) )
   user  system elapsed 
   0.23    0.00    0.23 
> identical(r1, r2)
[1] TRUE
11 голосов
/ 12 ноября 2009

Просто используйте цикл for. В любом случае любые встроенные функции выродятся в это, и вы потеряете ясность выражения, если вы тщательно не построите функцию, которая обобщает внешние для работы со списками.

Самое большое улучшение, которое вы могли бы сделать, - это предварительно выделить матрицу:

M <- list()
length(M) <- numElements ^ 2
dim(M) <- c(numElements, numElements)

PS. Список - это вектор.

4 голосов
/ 29 сентября 2015

Хотя это старый вопрос, здесь есть другое решение, которое больше соответствует духу внешней функции. Идея состоит в том, чтобы применять внешние по индексам list1 и list2:

cor2 <- Vectorize(function(x,y) {
   vec1 <- list1[[x]]
   vec2 <- list2[[y]]
   cor(vec1,vec2,method="spearman")
})
outer(1:length(list1), 1:length(list2), cor2)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...