Supposons que j'ai une matrice n par 2 et une fonction prenant un vecteur à 2 comme argument. Je voudrais appliquer la fonction à chaque ligne de la matrice et obtenir un n-vecteur. Comment faire cela en R?
Par exemple, j'aimerais 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?
Vous utilisez simplement la fonction 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>
Cela prend une matrice et applique une fonction (idiote) à chaque ligne. Vous transmettez des arguments supplémentaires à la fonction en tant que quatrième, cinquième, ... arguments à apply()
.
Si vous souhaitez appliquer des fonctions communes telles que somme ou moyenne, vous devez utiliser rowSums
ou rowMeans
car elles sont plus rapides que l'approche apply(data, 1, sum)
. Sinon, restez avec apply(data, 1, fun)
. Vous pouvez passer des arguments supplémentaires après les arguments 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
Ensuite, 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
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()
devait être transposé en utilisant t()
pour obtenir la même présentation 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
La première étape consisterait à créer l'objet de fonction, puis à l'appliquer. Si vous voulez un objet de matrice ayant le même nombre de lignes, vous pouvez le prédéfinir et utiliser le formulaire object [] comme illustré (sinon, la valeur renvoyé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 des paramètres autres que vos paramètres par défaut, l'appel doit inclure les 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 ainsi qu'un entier unique.
Appliquer fait bien le travail, mais est assez lent. Utiliser sapply et vapply pourrait être utile. Dplyr peut également être utile au niveau des lignes. Voyons un exemple de la manière de produire des produits en lignes de n'importe quel bloc de données.
a = data.frame(t(iris[1:10,1:3]))
vapply(a, prod, 0)
sapply(a, prod)
Notez qu'attribuer à une variable avant d'utiliser vapply/sapply/apply est une bonne pratique car cela réduit beaucoup le temps. Voyons les résultats des micro-indices
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é
Une autre approche si vous souhaitez utiliser une partie variable de l'ensemble de données au lieu d'une valeur unique consiste à utiliser rollapply(data, width, FUN, ...)
. L'utilisation d'un vecteur de largeurs vous permet d'appliquer une fonction sur une fenêtre variable du jeu de données. J'ai utilisé cela pour créer une routine de filtrage adaptatif, même si elle n'est pas très efficace.