2010-11-21 6 views
85

Angenommen, ich habe eine n von 2 Matrix und eine Funktion, die einen 2-Vektor als eines seiner Argumente nimmt. Ich möchte die Funktion auf jede Zeile der Matrix anwenden und einen n-Vektor erhalten. Wie macht man das in R?Wenden Sie eine Funktion auf jede Zeile einer Matrix oder eines Datenrahmens

Zum Beispiel würde Ich mag die Dichte eines 2D-Standardnormalverteilung auf drei Punkte berechnen:

bivariate.density(x = c(0, 0), mu = c(0, 0), sigma = c(1, 1), rho = 0){ 
    exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+x[2]^2/sigma[2]^2-2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2)) 
} 

out <- rbind(c(1, 2), c(3, 4), c(5, 6)) 

So wird die Funktion jeder Reihe von out bewerben?

Wie übergeben Sie Werte für die anderen Argumente neben den Punkten auf die Funktion in der Art, wie Sie angeben?

Antwort

136

Sie verwenden einfach die apply() Funktion:

R> M <- matrix(1:6, nrow=3, byrow=TRUE) 
R> M 
    [,1] [,2] 
[1,] 1 2 
[2,] 3 4 
[3,] 5 6 
R> apply(M, 1, function(x) 2*x[1]+x[2]) 
[1] 4 10 16 
R> 

Diese nimmt eine Matrix und wendet eine (dumme) Funktion zu jeder Zeile. Sie übergeben zusätzliche Argumente an die Funktion als vierte, fünfte, ... Argumente an apply().

+0

Dank verwendet werden! Was ist, wenn die Zeilen der Matrix nicht das erste Argument der Funktion sind? Wie spezifiziert man, welchem ​​Argument der Funktion jede Zeile der Matrix zugewiesen ist? – Tim

+0

Lesen Sie die Hilfe für 'apply()' - es wird nach Zeile durchsucht (wenn das zweite Argument 1 ist, sonst nach Spalte), und die aktuelle Zeile (oder Spalte) ist immer das erste Argument. So sind die Dinge definiert. –

+0

@Tim: Wenn Sie eine interne R-Funktion verwenden und die Zeile nicht die erste Arg ist, tun Sie wie Dirk und machen Sie Ihre eigene benutzerdefinierte Funktion, wo Zeile ** ist ** der erste Arg. –

6

Der erste Schritt wäre, das Funktionsobjekt zu erstellen und dann anzuwenden. Wenn Sie ein Matrix-Objekt mögen, dass die gleiche Anzahl von Zeilen hat, können Sie es vordefinieren und das Objekt [] Formular verwenden, wie dargestellt (ansonsten der zurückgegebene Wert wird auf einen Vektor vereinfacht werden):

bvnormdens <- function(x=c(0,0),mu=c(0,0), sigma=c(1,1), rho=0){ 
    exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+ 
          x[2]^2/sigma[2]^2- 
          2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 
    1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2)) 
    } 
out=rbind(c(1,2),c(3,4),c(5,6)); 

bvout<-matrix(NA, ncol=1, nrow=3) 
bvout[] <-apply(out, 1, bvnormdens) 
bvout 
      [,1] 
[1,] 1.306423e-02 
[2,] 5.931153e-07 
[3,] 9.033134e-15 

Wenn Sie wollen zu verwenden, anders als Ihre Standardparameter dann sollte der Anruf benannten Argumente nach der Funktion sind: auch auf höherdimensionalen Arrays

bvout[] <-apply(out, 1, FUN=bvnormdens, mu=c(-1,1), rho=0.6) 

apply() und die RAND verwendet werden Argument kann ein Vektor sowie eine einzige ganze Zahl sein .

14

Falls Sie häufig verwendete Funktionen wie Summe oder Mittelwert anzuwenden, Sie rowSums oder rowMeans verwenden sollten, da sie sind schneller als apply(data, 1, sum) Ansatz. Ansonsten bleib bei apply(data, 1, fun). Sie können zusätzliche Argumente nach FUN Argument übergeben (wie Dirk schon vorgeschlagen):

set.seed(1) 
m <- matrix(round(runif(20, 1, 5)), ncol=4) 
diag(m) <- NA 
m 
    [,1] [,2] [,3] [,4] 
[1,] NA 5 2 3 
[2,] 2 NA 2 4 
[3,] 3 4 NA 5 
[4,] 5 4 3 NA 
[5,] 2 1 4 4 

Dann sind Sie so etwas tun kann:

apply(m, 1, quantile, probs=c(.25,.5, .75), na.rm=TRUE) 
    [,1] [,2] [,3] [,4] [,5] 
25% 2.5 2 3.5 3.5 1.75 
50% 3.0 2 4.0 4.0 3.00 
75% 4.0 3 4.5 4.5 4.00 
2

Ein weiterer Ansatz, wenn Sie einen unterschiedlichen Teil des Datensatzes verwendet werden soll anstelle eines einzelnen Wertes ist rollapply(data, width, FUN, ...) zu verwenden. Wenn Sie einen Vektor mit Breiten verwenden, können Sie eine Funktion auf ein variierendes Fenster des Datensatzes anwenden. Ich habe dies verwendet, um eine adaptive Filterroutine zu erstellen, obwohl es nicht sehr effizient ist.

11

Hier ist ein kurzes Beispiel zum Anwenden einer Funktion auf jede Zeile einer Matrix. (Hier wandte die Funktion jede Zeile auf 1 normiert)

Hinweis: Das Ergebnis aus den apply() hatte t() mit Recht umgesetzt sein, das gleiche Layout wie die Eingabematrix A zu erhalten.

A <- matrix(c(
    0, 1, 1, 2, 
    0, 0, 1, 3, 
    0, 0, 1, 3 
), nrow = 3, byrow = TRUE) 

t(apply(A, 1, function(x) x/sum(x))) 

Ergebnis:

 [,1] [,2] [,3] [,4] 
[1,] 0 0.25 0.25 0.50 
[2,] 0 0.00 0.25 0.75 
[3,] 0 0.00 0.25 0.75 
3

Nehmen macht den Job gut, aber es ist ziemlich langsam. Verwendung von Sapply und vapply könnte nützlich sein. dplyr's rowwise könnte auch nützlich sein Lassen Sie uns ein Beispiel sehen, wie man reihenweises Produkt von irgendeinem Datenrahmen macht.

a = data.frame(t(iris[1:10,1:3])) 
vapply(a, prod, 0) 
sapply(a, prod) 

Beachten Sie, dass vor dem Variablen zugewiesen wird mit vapply/sapply/apply ist eine gute Praxis, wie es an der Zeit eine Menge reduziert. Mal sehen, ergibt-Micro

a = data.frame(t(iris[1:10,1:3])) 
b = iris[1:10,1:3] 
microbenchmark::microbenchmark(
    apply(b, 1 , prod), 
    vapply(a, prod, 0), 
    sapply(a, prod) , 
    apply(iris[1:10,1:3], 1 , prod), 
    vapply(data.frame(t(iris[1:10,1:3])), prod, 0), 
    sapply(data.frame(t(iris[1:10,1:3])), prod) , 
    b %>% rowwise() %>% 
     summarise(p = prod(Sepal.Length,Sepal.Width,Petal.Length)) 
) 

Haben Sie einen sorgfältigen Blick auf, wie t() ist