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:

  1. Exiger moins de code pour écrire.
  2. N'a pas de compteur d'itération.
  3. 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 la mean 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)
    

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 que lapply() mais tente de simplifier la sortie vers un vecteur ou une matrice.
    • vapply() = une variante de sapply() dans laquelle le type de l'objet en sortie doit être spécifié.
  • mapply() = comme lapply() mais peut transmettre plusieurs vecteurs en entrée de la fonction spécifiée. Peut être simplifié comme sapply() .
    • Map() est un alias mapply() avec SIMPLIFY = 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


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow