R Language
* appliquer une famille de fonctions (fonctionnelles)
Recherche…
Remarques
Une fonction dans la famille *apply
est une abstraction d'une boucle for
. Par rapport aux boucles for
*apply
fonctions d’ *apply
présentent les avantages suivants:
- Exiger moins de code pour écrire.
- N'a pas de compteur d'itération.
- N'utilise pas de variables temporaires pour stocker des résultats intermédiaires.
Cependant for
les boucles sont plus générales et peuvent nous donner plus de contrôle permettant de réaliser des calculs complexes qui ne sont pas toujours trivial de le faire en utilisant *apply
des fonctions.
La relation entre les boucles for
et *apply
fonctions *apply
est expliquée dans la documentation de for
loops .
Membres de la famille *apply
La famille de fonctions *apply
contient plusieurs variantes du même principe qui diffèrent principalement en fonction du type de sortie qu'elles renvoient.
fonction | Contribution | Sortie |
---|---|---|
apply | matrix , data.frame ou array | vecteur ou matrice (en fonction de la longueur de chaque élément retourné) |
sapply | vecteur ou list | vecteur ou matrice (en fonction de la longueur de chaque élément retourné) |
lapply | vecteur ou list | list |
vapply | vecteur ou `liste | vecteur ou matrice (en fonction de la longueur de chaque élément retourné) de la classe désignée par l'utilisateur |
mapply | plusieurs vecteurs, lists ou une combinaison | list |
Voir "Exemples" pour voir comment chacune de ces fonctions est utilisée.
Utiliser des fonctions anonymes avec apply
apply
est utilisé pour évaluer une fonction (peut-être anonyme) sur les marges d'un tableau ou d'une matrice.
Utilisons le jeu de données iris
pour illustrer cette idée. Le jeu de données iris
a des mesures de 150 fleurs de 3 espèces. Voyons comment est structuré cet ensemble de données:
> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
Maintenant, imaginez que vous souhaitiez connaître la moyenne de chacune de ces variables. Une façon de résoudre ce problème pourrait être d'utiliser une boucle for
, mais les programmeurs préféreront souvent utiliser apply
(pour des raisons, voir Remarques):
> apply(iris[1:4], 2, mean)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
- Dans le premier paramètre, nous définissons
iris
pour n'inclure que les 4 premières colonnes, car lamean
ne fonctionne que sur les données numériques. - La deuxième valeur de paramètre de
2
indique que nous voulons travailler uniquement sur les colonnes (le deuxième indice du tableau r × c);1
donnerait la ligne signifie.
De la même manière, nous pouvons calculer des valeurs plus significatives:
# standard deviation
apply(iris[1:4], 2, sd)
# variance
apply(iris[1:4], 2, var)
Attention : R possède des fonctions intégrées qui sont mieux colMeans
au calcul des sommes et des moyens des colonnes et des lignes: colMeans
et rowMeans
.
Faisons maintenant une tâche différente et plus significative: calculons la moyenne uniquement pour les valeurs supérieures à 0.5
. Pour cela, nous allons créer notre propre fonction mean
.
> our.mean.function <- function(x) { mean(x[x > 0.5]) }
> apply(iris[1:4], 2, our.mean.function)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.665347
(Notez la différence dans la moyenne de Petal.Width
)
Mais, si nous ne voulons pas utiliser cette fonction dans le reste de notre code? Ensuite, nous pouvons utiliser une fonction anonyme et écrire notre code comme ceci:
apply(iris[1:4], 2, function(x) { mean(x[x > 0.5]) })
Ainsi, comme nous l'avons vu, nous pouvons utiliser apply
pour exécuter la même opération sur des colonnes ou des lignes d'un jeu de données en utilisant une seule ligne.
Avertissement : Puisque apply
renvoie des types de sortie très différents en fonction de la longueur des résultats de la fonction spécifiée, cela peut ne pas être le meilleur choix dans les cas où vous ne travaillez pas de manière interactive. Les autres fonctions de la famille *apply
autres sont un peu plus prévisibles (voir Remarques).
Chargement en bloc de fichiers
pour un grand nombre de fichiers qui peuvent devoir être utilisés dans un processus similaire et avec des noms de fichiers bien structurés.
Tout d'abord, un vecteur des noms de fichiers à accéder doit être créé, il y a plusieurs options pour cela:
Création manuelle du vecteur avec
paste0()
files <- paste0("file_", 1:100, ".rds")
L'utilisation de
list.files()
avec un terme de recherche regex pour le type de fichier nécessite la connaissance des expressions régulières ( regex ) si d'autres fichiers du même type sont dans le répertoire.files <- list.files("./", pattern = "\\.rds$", full.names = TRUE)
où X
est un vecteur d'une partie du format de nommage des fichiers utilisé.
lapply
affichera chaque réponse en tant qu'élément d'une liste.
readRDS
est spécifique aux fichiers .rds
et changera en fonction de l'application du processus.
my_file_list <- lapply(files, readRDS)
Ce n'est pas nécessairement plus rapide qu'une boucle for testing mais permet à tous les fichiers d'être un élément d'une liste sans les assigner explicitement.
Enfin, nous avons souvent besoin de charger plusieurs paquets à la fois. Cette astuce peut facilement le faire en appliquant library()
à toutes les bibliothèques que nous souhaitons importer:
lapply(c("jsonlite","stringr","igraph"),library,character.only=TRUE)
Combiner plusieurs `data.frames` (` lapply`, `mapply`)
Dans cet exercice, nous allons générer quatre modèles de régression linéaire bootstrap et combiner les résumés de ces modèles dans un seul bloc de données.
library(broom)
#* Create the bootstrap data sets
BootData <- lapply(1:4,
function(i) mtcars[sample(1:nrow(mtcars),
size = nrow(mtcars),
replace = TRUE), ])
#* Fit the models
Models <- lapply(BootData,
function(BD) lm(mpg ~ qsec + wt + factor(am),
data = BD))
#* Tidy the output into a data.frame
Tidied <- lapply(Models,
tidy)
#* Give each element in the Tidied list a name
Tidied <- setNames(Tidied, paste0("Boot", seq_along(Tidied)))
À ce stade, nous pouvons adopter deux approches pour insérer les noms dans le data.frame.
#* Insert the element name into the summary with `lapply`
#* Requires passing the names attribute to `lapply` and referencing `Tidied` within
#* the applied function.
Described_lapply <-
lapply(names(Tidied),
function(nm) cbind(nm, Tidied[[nm]]))
Combined_lapply <- do.call("rbind", Described_lapply)
#* Insert the element name into the summary with `mapply`
#* Allows us to pass the names and the elements as separate arguments.
Described_mapply <-
mapply(
function(nm, dframe) cbind(nm, dframe),
names(Tidied),
Tidied,
SIMPLIFY = FALSE)
Combined_mapply <- do.call("rbind", Described_mapply)
Si vous êtes un fan des magrittr
style magrittr
, vous pouvez accomplir la tâche entière dans une seule chaîne (bien qu'il ne soit pas prudent de le faire si vous avez besoin des objets intermédiaires, tels que les objets modèles eux-mêmes):
library(magrittr)
library(broom)
Combined <- lapply(1:4,
function(i) mtcars[sample(1:nrow(mtcars),
size = nrow(mtcars),
replace = TRUE), ]) %>%
lapply(function(BD) lm( mpg ~ qsec + wt + factor(am), data = BD)) %>%
lapply(tidy) %>%
setNames(paste0("Boot", seq_along(.))) %>%
mapply(function(nm, dframe) cbind(nm, dframe),
nm = names(.),
dframe = .,
SIMPLIFY = FALSE) %>%
do.call("rbind", .)
Utilisation de fonctionnalités intégrées
Fonctions fonctionnelles intégrées: lapply (), sapply () et mapply ()
R est livré avec des fonctions intégrées, dont les plus connues sont peut-être la famille des fonctions d'application. Voici une description de certaines des fonctions les plus courantes:
-
lapply()
= prend une liste en argument et applique la fonction spécifiée à la liste. -
sapply()
= le même quelapply()
mais tente de simplifier la sortie vers un vecteur ou une matrice.-
vapply()
= une variante desapply()
dans laquelle le type de l'objet en sortie doit être spécifié.
-
-
mapply()
= commelapply()
mais peut transmettre plusieurs vecteurs en entrée de la fonction spécifiée. Peut être simplifié commesapply()
.-
Map()
est un aliasmapply()
avecSIMPLIFY = FALSE
.
-
lapply ()
lapply()
peut être utilisé avec deux itérations différentes:
-
lapply(variable, FUN)
-
lapply(seq_along(variable), FUN)
# Two ways of finding the mean of x
set.seed(1)
df <- data.frame(x = rnorm(25), y = rnorm(25))
lapply(df, mean)
lapply(seq_along(df), function(x) mean(df[[x]))
sapply ()
sapply()
tentera de résoudre sa sortie en un vecteur ou une matrice.
# Two examples to show the different outputs of sapply()
sapply(letters, print) ## produces a vector
x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE))
sapply(x, quantile) ## produces a matrix
mapply ()
mapply()
fonctionne beaucoup comme lapply()
sauf qu'il peut prendre plusieurs vecteurs en entrée (d'où le m pour multivarié).
mapply(sum, 1:5, 10:6, 3) # 3 will be "recycled" by mapply
Utilisation de fonctions définies par l'utilisateur
Fonctionnalités définies par l'utilisateur
Les utilisateurs peuvent créer leurs propres fonctionnels à divers degrés de complexité. Les exemples suivants proviennent de Functionals by Hadley Wickham:
randomise <- function(f) f(runif(1e3))
lapply2 <- function(x, f, ...) {
out <- vector("list", length(x))
for (i in seq_along(x)) {
out[[i]] <- f(x[[i]], ...)
}
out
}
Dans le premier cas, randomise
accepte un seul argument f
et l'appelle sur un échantillon de variables aléatoires uniformes. Pour démontrer l'équivalence, nous appelons set.seed
ci-dessous:
set.seed(123)
randomise(mean)
#[1] 0.4972778
set.seed(123)
mean(runif(1e3))
#[1] 0.4972778
set.seed(123)
randomise(max)
#[1] 0.9994045
set.seed(123)
max(runif(1e3))
#[1] 0.9994045
Le second exemple est une ré-implémentation de base::lapply
, qui utilise des fonctions pour appliquer une opération ( f
) à chaque élément d'une liste ( x
). Le paramètre ...
permet à l'utilisateur de transmettre des arguments supplémentaires à f
, tels que l'option na.rm
dans la fonction mean
:
lapply(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
#
# [[2]]
# [1] NA
lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
#
# [[2]]
# [1] NA
lapply(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
#
# [[2]]
# [1] 4
lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
#
# [[2]]
# [1] 4