2010-11-21 11 views
85

Supponiamo di avere una matrice n di 2 e una funzione che accetta un vettore 2 come uno dei suoi argomenti. Vorrei applicare la funzione a ciascuna riga della matrice e ottenere un vettore n. Come fare questo in R?Applicare una funzione a ogni riga di una matrice o di un frame di dati

Per 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 per ogni fila di out?

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

risposta

136

si utilizza semplicemente la funzione apply():

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 (stupido) per ogni riga. Passi argomenti extra alla funzione come quarto, quinto, ... argomenti a apply().

+0

Grazie! Cosa succede se le righe della matrice non sono il primo argomento della funzione? Come specificare a quale arg di funzione è assegnata ogni riga della matrice? – Tim

+0

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. Ecco come sono definite le cose. –

+0

@Tim: se usi una funzione R interna e la riga non è il primo argomento, fallo come Dirk e crea la tua funzione personalizzata dove la riga ** è ** il primo argomento. –

6

Il primo passaggio sarebbe rendere l'oggetto funzione, quindi applicarlo. Se si desidera un oggetto matrice che ha lo stesso numero di righe, è possibile predefinire e utilizzare l'oggetto [] modulo come illustrato (altrimenti il ​​valore restituito verrà semplificato di 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 voleva ad uso diverso da quello i parametri di default, allora la chiamata dovrebbe includere argomenti denominati dopo la funzione:

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

applicare() può essere utilizzato anche su matrici di dimensione superiore e l'argomento margine può essere un vettore così come un singolo intero .

14

Se si desidera applicare funzioni comuni come somma o media, è necessario utilizzare rowSums o rowMeans poiché sono più veloci dell'approccio apply(data, 1, sum). Altrimenti, continua con apply(data, 1, fun). È possibile passare ulteriori argomenti che seguono argomento FUN (come Dirk già suggerito):

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 

allora si può fare qualcosa di simile:

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

Un altro approccio, se si desidera utilizzare una parte variabile del set di dati invece di un singolo valore si usa rollapply(data, width, FUN, ...). L'utilizzo di un vettore di larghezze consente di applicare una funzione su una finestra variabile dell'insieme di dati. Ho usato questo per costruire una routine di filtraggio adattivo, anche se non è molto efficiente.

11

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

Nota: Il risultato della apply() doveva essere trasposti utilizzando t() per ottenere la stessa disposizione della matrice di ingresso 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 
3

Applicare fa il lavoro bene, ma è piuttosto lento. L'uso sapientamente e vappamente potrebbe essere utile. potrebbe anche essere utile il file row di dplyr Vediamo un esempio di come fare un prodotto di riga di qualsiasi frame di dati.

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

Si noti che l'assegnazione alla variabile prima di utilizzare vapply/sapply/applicare è buona pratica in quanto riduce il tempo molto. Vediamo microbenchmark risultati

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

Avere uno sguardo attento a come t() viene utilizzato