Applica una funzione a ogni riga di una matrice o di un data frame


130

Supponiamo di avere una matrice per 2 e una funzione che accetta un 2-vettore come uno dei suoi argomenti. Vorrei applicare la funzione a ciascuna riga della matrice e ottenere un vettore n. Come farlo in R?

Ad esempio, vorrei calcolare la densità di una distribuzione Normale standard 2D su tre punti:

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

Come applicare la funzione a ciascuna riga di out?

Come passare i valori per gli altri argomenti oltre ai punti alla funzione nel modo specificato?

Risposte:


181

Usa semplicemente la apply()funzione:

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> 

Questo richiede una matrice e applica una funzione (sciocca) a ciascuna riga. Si passano argomenti extra alla funzione come quarto, quinto, ... argomenti a apply().


Grazie! E se le righe della matrice non fossero il primo argomento della funzione? Come specificare a quale argomento della funzione è assegnata ciascuna riga della matrice?
Tim

Leggi la guida per apply(): scorre per riga (quando il secondo argomento è 1, altrimenti per colonna) e la riga corrente (o colonna) è sempre il primo argomento. È così che vengono definite le cose.
Dirk Eddelbuettel

@Tim: se usi una funzione R interna e la riga non è il primo argomento, fai come ha fatto Dirk e crea la tua funzione personalizzata dove riga è il primo argomento.
Joris Meys

3
Il pacchetto plyr fornisce un'ampia gamma di questi tipi di funzioni. Fornisce inoltre più funzionalità, inclusa l'elaborazione parallela.
Paul Hiemstra

6
@ cryptic0 questa risposta è in ritardo, ma per i googler, il secondo argomento in apply è l' MARGINargomento. Qui significa applicare la funzione alle righe (la prima dimensione in dim(M)). Se fosse 2, applicherebbe la funzione alle colonne.
De Novo

17

Nel caso in cui desideri applicare funzioni comuni come somma o media, dovresti usare rowSumso rowMeanspoiché sono più veloci apply(data, 1, sum)dell'approccio. Altrimenti, resta con apply(data, 1, fun). Puoi passare argomenti aggiuntivi dopo l'argomento FUN (come già suggerito da Dirk):

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

Quindi puoi fare qualcosa del genere:

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

Ecco un breve esempio di applicazione di una funzione a ciascuna riga di una matrice. (Qui, la funzione applicata normalizza ogni riga a 1.)

Nota: il risultato di apply()doveva essere trasposto utilizzando t()per ottenere lo stesso layout della matrice di input 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) ))

Risultato:

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

Il primo passo sarebbe creare l'oggetto funzione, quindi applicarlo. Se desideri un oggetto matrice che abbia lo stesso numero di righe, puoi predefinirlo e utilizzare la forma oggetto [] come illustrato (altrimenti il ​​valore restituito verrà semplificato in un vettore):

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

Se si desidera utilizzare parametri diversi da quelli predefiniti, la chiamata dovrebbe includere argomenti denominati dopo la funzione:

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

apply () può essere utilizzato anche su array di dimensioni superiori e l'argomento MARGIN può essere un vettore oltre che un singolo intero.


4

L'applicazione fa bene il lavoro, ma è piuttosto lenta. Usare sapply e vapply potrebbe essere utile. Anche il rowwise di dplyr potrebbe essere utile Vediamo un esempio di come creare un prodotto per riga di qualsiasi data frame.

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

Si noti che l'assegnazione a una variabile prima di utilizzare vapply / sapply / apply è una buona pratica in quanto riduce molto il tempo. Vediamo i risultati del microbenchmark

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

Guarda attentamente come viene utilizzato t ()


Potrebbe essere più giusto confrontare la famiglia di candidature se hai usato b <- t(iris[1:10, 1:3])e apply(b, 2 prod).
DaSpeeg

2

Un altro approccio se si desidera utilizzare una parte variabile del set di dati anziché un singolo valore è quello di utilizzare rollapply(data, width, FUN, ...). L'utilizzo di un vettore di larghezze consente di applicare una funzione su una finestra variabile del set di dati. L'ho usato per creare una routine di filtraggio adattiva, sebbene non sia molto efficiente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.