Appliquer une fonction à chaque ligne d'une matrice ou d'un bloc de données


130

Supposons que j'ai une matrice par 2 et une fonction qui prend un vecteur 2 comme l'un de ses arguments. Je voudrais appliquer la fonction à chaque ligne de la matrice et obtenir un vecteur n. Comment faire cela en R?

Par exemple, je voudrais calculer la densité d'une distribution normale standard 2D sur trois points:

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

Comment appliquer la fonction à chaque ligne de out?

Comment passer des valeurs pour les autres arguments en plus des points à la fonction de la manière que vous spécifiez?

Réponses:


181

Vous utilisez simplement la apply()fonction:

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> 

Cela prend une matrice et applique une fonction (idiote) à chaque ligne. Vous passez des arguments supplémentaires à la fonction en tant que quatrième, cinquième, ... arguments à apply().


Merci! Que faire si les lignes de la matrice ne sont pas le premier argument de la fonction? Comment spécifier à quel argument de la fonction chaque ligne de la matrice est affectée?
Tim

Lisez l'aide pour apply()- il balaie par ligne (lorsque le second argument est 1, sinon par colonne), et la ligne courante (ou col) est toujours le premier argument. C'est ainsi que les choses sont définies.
Dirk Eddelbuettel

@Tim: si vous utilisez une fonction R interne et que la ligne n'est pas le premier argument, faites comme Dirk et créez votre propre fonction personnalisée où la ligne est le premier argument.
Joris Meys

3
Le package plyr fournit une large gamme de ces types de fonctions. Il fournit également plus de fonctionnalités, y compris le traitement parallèle.
Paul Hiemstra

6
@ cryptic0 cette réponse est tardive, mais pour les googleurs, le deuxième argument de apply est l' MARGINargument. Ici, cela signifie appliquer la fonction aux lignes (la première dimension dans dim(M)). Si c'était 2, il appliquerait la fonction aux colonnes.
De Novo

17

Si vous souhaitez appliquer des fonctions courantes telles que la somme ou la moyenne, vous devez utiliser rowSumsou rowMeanscar elles sont plus rapides que l' apply(data, 1, sum)approche. Sinon, restez avec apply(data, 1, fun). Vous pouvez passer des arguments supplémentaires après l'argument FUN (comme Dirk déjà suggéré):

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

Ensuite, vous pouvez faire quelque chose comme ceci:

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

Voici un court exemple d'application d'une fonction à chaque ligne d'une matrice. (Ici, la fonction appliquée normalise chaque ligne à 1.)

Remarque: Le résultat de la apply()a dû être transposé en utilisant t()pour obtenir la même disposition que la matrice d'entrée 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) ))

Résultat:

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

La première étape serait de créer l'objet de fonction, puis de l'appliquer. Si vous voulez un objet matrice qui a le même nombre de lignes, vous pouvez le prédéfinir et utiliser la forme object [] comme illustré (sinon la valeur retournée sera simplifiée en vecteur):

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

Si vous souhaitez utiliser d'autres paramètres que vos paramètres par défaut, l'appel doit inclure des arguments nommés après la fonction:

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

apply () peut également être utilisé sur des tableaux de dimensions supérieures et l'argument MARGIN peut être un vecteur ainsi qu'un seul entier.


4

Postuler fait bien le travail, mais est assez lent. Utiliser sapply et vapply pourrait être utile. rowwise de dplyr pourrait également être utile Voyons un exemple de la façon de produire un produit ligne par ligne de n'importe quelle trame de données.

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

Notez que l'assignation à une variable avant d'utiliser vapply / sapply / apply est une bonne pratique car elle réduit beaucoup le temps. Voyons les résultats de 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))
)

Regardez attentivement comment t () est utilisé


Il pourrait être plus juste de comparer la famille d'application si vous avez utilisé b <- t(iris[1:10, 1:3])et apply(b, 2 prod).
DaSpeeg

2

Une autre approche si vous souhaitez utiliser une partie variable de l'ensemble de données au lieu d'une seule valeur consiste à utiliser rollapply(data, width, FUN, ...). L'utilisation d'un vecteur de largeurs vous permet d'appliquer une fonction sur une fenêtre variable de l'ensemble de données. J'ai utilisé cela pour créer une routine de filtrage adaptatif, même si ce n'est pas très efficace.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.