152 votes

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

Supposons que j'ai une matrice n par 2 et une fonction qui prend un vecteur 2 comme l'un de ses arguments. J'aimerais 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 transmettre les valeurs des autres arguments, en plus des points, à la fonction de la manière que vous indiquez ?

201voto

Dirk Eddelbuettel Points 134700

Vous utilisez simplement le 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> 

Ceci prend une matrice et applique une fonction (stupide) à chaque ligne. Vous passez des arguments supplémentaires à la fonction comme quatrième, cinquième, ... arguments à apply() .

0 votes

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

0 votes

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

1 votes

@Tim : si vous utilisez une fonction interne de R et que la ligne n'est pas la première arg, faites comme Dirk et créez votre propre fonction personnalisée où row est la première arg.

18voto

aL3xa Points 10236

Si vous souhaitez appliquer des fonctions courantes telles que la somme ou la moyenne, vous devez utiliser la méthode suivante rowSums o rowMeans puisqu'ils sont plus rapides que apply(data, 1, sum) approche. Sinon, restez-en à apply(data, 1, fun) . Vous pouvez passer des arguments supplémentaires après l'argument FUN (comme Dirk l'a 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

Alors vous pouvez faire quelque chose comme ça :

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

17voto

Viliam Simko Points 984

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

Note : Le résultat de la apply() devait ê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

7voto

Pratham Points 119

Apply fait bien le travail, mais est assez lent. L'utilisation de sapply et vapply peut être utile. rowwise de dplyr peut également être utile. Voyons un exemple de comment faire un produit par rangée de n'importe quel cadre 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/applic est une bonne pratique car elle réduit considérablement le temps. Voyons les résultats du 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é.

0 votes

Il serait peut-être plus juste de comparer la famille de demandes si vous utilisiez b <- t(iris[1:10, 1:3]) y apply(b, 2 prod) .

6voto

BondedDust Points 105234

La première étape consisterait à fabriquer l'objet fonction, puis à 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 ceux 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 dimension supérieure et l'argument MARGIN peut être un vecteur aussi bien qu'un simple entier.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X