Wenden Sie eine Funktion auf jede Zeile einer Matrix oder eines Datenrahmens an


129

Angenommen, ich habe eine by 2-Matrix und eine Funktion, die einen 2-Vektor als eines ihrer Argumente verwendet. Ich möchte die Funktion auf jede Zeile der Matrix anwenden und einen n-Vektor erhalten. Wie geht das in R?

Zum Beispiel möchte ich die Dichte einer 2D-Standardnormalverteilung auf drei Punkten 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))

Wie wende ich die Funktion auf jede Zeile von an out?

Wie übergebe ich Werte für die anderen Argumente neben den Punkten in der von Ihnen angegebenen Weise an die Funktion?

Antworten:


180

Sie benutzen 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> 

Dies nimmt eine Matrix und wendet eine (dumme) Funktion auf jede Zeile an. Sie übergeben der Funktion zusätzliche Argumente als viertes, fünftes, ... Argument an apply().


Vielen Dank! Was ist, wenn die Zeilen der Matrix nicht das erste Argument der Funktion sind? Wie kann man angeben, welchem ​​Argument der Funktion jede Zeile der Matrix zugeordnet ist?
Tim

Lesen Sie die Hilfe für apply()- es wird nach Zeilen gewobbelt (wenn das zweite Argument 1 ist, sonst nach Spalten), und die aktuelle Zeile (oder Spalte) ist immer das erste Argument. So werden Dinge definiert.
Dirk Eddelbuettel

@ Tim: Wenn Sie eine interne R - Funktion verwenden und die Zeile ist nicht das erste arg, tun , wie Dirk Ihre eigene benutzerdefinierte Funktion haben und machen , wo Reihe ist die erste arg.
Joris Meys

3
Das Plyr-Paket bietet eine breite Palette dieser Anwendungsfunktionen. Es bietet auch mehr Funktionen, einschließlich paralleler Verarbeitung.
Paul Hiemstra

6
@ cryptic0 Diese Antwort ist spät, aber für Googler ist das zweite Argument in apply das MARGINArgument. Hier bedeutet dies, dass die Funktion auf die Zeilen angewendet wird (die erste Dimension in dim(M)). Wenn es 2 wäre, würde es die Funktion auf die Spalten anwenden.
De Novo

17

Wenn Sie allgemeine Funktionen wie Summe oder Mittelwert anwenden möchten, sollten Sie diese verwenden rowSumsoder rowMeansda sie schneller als der apply(data, 1, sum)Ansatz sind. Ansonsten bleib bei apply(data, 1, fun). Sie können zusätzliche Argumente nach dem FUN-Argument übergeben (wie Dirk bereits vorgeschlagen hat):

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 können Sie so etwas tun:

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

15

Hier ist ein kurzes Beispiel für die Anwendung einer Funktion auf jede Zeile einer Matrix. (Hier normalisiert die angewendete Funktion jede Zeile auf 1.)

Hinweis: Das Ergebnis von apply()musste mit transponiert werden t(), um das gleiche Layout wie die Eingabematrix zu erhalten A.

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

6

Der erste Schritt wäre, das Funktionsobjekt zu erstellen und es dann anzuwenden. Wenn Sie ein Matrixobjekt mit der gleichen Anzahl von Zeilen möchten, können Sie es vordefinieren und das Objekt [] -Formular wie abgebildet verwenden (andernfalls wird der zurückgegebene Wert zu einem Vektor vereinfacht):

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 andere als Ihre Standardparameter verwenden möchten, sollte der Aufruf benannte Argumente nach der Funktion enthalten:

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

apply () kann auch für höherdimensionale Arrays verwendet werden, und das MARGIN-Argument kann sowohl ein Vektor als auch eine einzelne Ganzzahl sein.


4

Bewerben macht den Job gut, ist aber ziemlich langsam. Die Verwendung von sapply und vapply könnte nützlich sein. dplyrs zeilenweise könnte auch nützlich sein. Sehen wir uns ein Beispiel an, wie ein zeilenweises Produkt eines Datenrahmens erstellt wird.

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

Beachten Sie, dass das Zuweisen zu Variablen vor der Verwendung von vapply / sapply / apply eine gute Vorgehensweise ist, da dies die Zeit erheblich verkürzt. Sehen wir uns die Ergebnisse der Mikrobenchmark an

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

Schauen Sie sich genau an, wie t () verwendet wird


Es könnte fairer sein, die betreffende Familie zu vergleichen, wenn Sie b <- t(iris[1:10, 1:3])und verwendet haben apply(b, 2 prod).
DaSpeeg

2

Ein anderer Ansatz, wenn Sie einen variierenden Teil des Datasets anstelle eines einzelnen Werts verwenden möchten, ist die Verwendung rollapply(data, width, FUN, ...). Durch die Verwendung eines Vektors mit Breiten können Sie eine Funktion auf ein variierendes Fenster des Datasets anwenden. Ich habe dies verwendet, um eine adaptive Filterroutine zu erstellen, obwohl sie nicht sehr effizient ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.