11 Stimmen

outer()-Äquivalent für Nicht-Vektorlisten in R

Ich weiß, wie outer() in R funktioniert:

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

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

Man nimmt 2 Vektoren, ermittelt das Kreuzprodukt dieser Vektoren und wendet dann die Funktion auf jedes Paar des Kreuzprodukts an.

Ich habe jedoch keine zwei Vektoren. Ich habe zwei Listen mit Matrizen:

M = Liste();

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

Und ich möchte eine Operation auf meiner Liste der Matrikeln durchführen. Das möchte ich tun:

outer(M, M, "*")

In diesem Fall möchte ich das Punktprodukt jeder Kombination von Matrizen nehmen, die ich habe.

Eigentlich versuche ich, eine Kernel-Matrix zu erzeugen (und ich habe eine Kernel-Funktion geschrieben), also möchte ich das tun:

outer(M, M, kernelFunction)

donde kernelFunction berechnet einen Abstand zwischen meinen beiden Matrizen.

Das Problem ist, dass outer() nur "Vektor"-Argumente annimmt, nicht aber "Liste "s usw. Gibt es eine Funktion, die das Äquivalent von outer() für Nicht-Vektor-Entitäten tut?

Alternativ dazu könnte ich eine for-Schleife verwenden, um dies zu tun:

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

aber ich versuche, dies zugunsten eines R-Konstrukts zu vermeiden (das möglicherweise effizienter ist). (Ja, ich weiß, dass ich die for-Schleife ändern kann, um die Diagonalmatrix zu berechnen und 50 % der Berechnungen zu sparen. Aber das ist nicht der Code, den ich zu optimieren versuche!)

Ist dies möglich? Irgendwelche Gedanken/Vorschläge?

16voto

Tommy Punkte 38569

Die äußere Funktion funktioniert tatsächlich mit Listen, aber die Funktion, die Sie bereitstellen, wiederholt die beiden Eingangsvektoren, so dass sie alle möglichen Kombinationen enthalten...

Als für die schneller ist, kombiniert äußere mit vapply ist 3x schneller als die doppelte for-Schleife auf meiner Maschine. Wenn die eigentliche Kernel-Funktion "echte Arbeit" leistet, ist der Unterschied in der Schleifengeschwindigkeit wahrscheinlich nicht so wichtig.

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

12voto

hadley Punkte 97925

Verwenden Sie einfach die for-Schleife. Alle eingebauten Funktionen werden sowieso dazu degenerieren, und Sie werden die Klarheit des Ausdrucks verlieren, es sei denn, Sie bauen sorgfältig eine Funktion, die äußere verallgemeinert, um mit Listen zu arbeiten.

Die größte Verbesserung, die Sie vornehmen könnten, wäre eine Vorabzuweisung der Matrix:

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

PS. Eine Liste ist ein Vektor.

5voto

thc Punkte 9208

Obwohl es sich um eine alte Frage handelt, hier eine andere Lösung, die mehr im Sinne der äußeren Funktion ist. Die Idee ist, outer entlang der Indizes von list1 und list2 anzuwenden:

cor2 <- Vectorize(function(x,y) {
   vec1 <- list1[[x]]
   vec2 <- list2[[y]]
   cor(vec1,vec2,method="spearman")
})
outer(1:length(list1), 1:length(list2), cor2)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X