Y a-t-il une raison pour laquelle je devrais utiliser
map(<list-like-object>, function(x) <do stuff>)
au lieu de
lapply(<list-like-object>, function(x) <do stuff>)
la sortie devrait être la même et les repères que j'ai établis semblent montrer que lapply
est légèrement plus rapide (il devrait être comme map
doit évaluer toutes les entrées d'évaluation non standard).
Donc, y a-t-il une raison pour laquelle, pour de tels cas simples, je devrais réellement envisager de passer à purrr::map
? Je ne pose pas ici la question de ce que l’on aime ou n’aime pas à propos de la syntaxe, des autres fonctionnalités fournies par purrr, etc., mais strictement de la comparaison de purrr::map
avec lapply
en utilisant l’évaluation standard, c.-à-d. map(<list-like-object>, function(x) <do stuff>)
. Existe-t-il un avantage de purrr::map
en termes de performances, de gestion des exceptions, etc.? Les commentaires ci-dessous suggèrent que ce n'est pas le cas, mais peut-être que quelqu'un pourrait en dire un peu plus?
Si la seule fonction que vous utilisez avec purrr est map()
, alors non, les avantages ne sont pas substantiels. Comme le souligne Rich Pauloo, le principal avantage de map()
réside dans les aides qui vous permettent d'écrire du code compact pour les cas particuliers les plus courants:
~ . + 1
est équivalent à function(x) x + 1
list("x", 1)
est équivalent à function(x) x[["x"]][[1]]
. Ces aides sont un peu plus générales que [[
- voir ?pluck
pour plus de détails. Pour data rectangling , l'argument .default
est particulièrement utile.
Mais la plupart du temps, vous n'utilisez pas une seule fonction *apply()
/map()
, vous en utilisez plusieurs, et l'avantage de purrr est une cohérence beaucoup plus grande entre les fonctions. Par exemple:
Le premier argument de lapply()
est la donnée; le premier argument de mapply()
est la fonction. Le premier argument de toutes les fonctions de la carte est toujours les données.
Avec vapply()
, sapply()
et mapply()
, vous pouvez choisir de supprimer les noms sur la sortie avec USE.NAMES = FALSE
; mais lapply()
n'a pas cet argument.
Il n'y a aucun moyen cohérent de transmettre des arguments cohérents à la fonction mappeur. La plupart des fonctions utilisent ...
mais mapply()
utilise MoreArgs
(ce qui devrait s'appeler MORE.ARGS
) et Map()
, Filter()
et Reduce()
attendez-vous à créer une nouvelle fonction anonyme. Dans les fonctions de la carte, les arguments constants viennent toujours après le nom de la fonction.
Presque toutes les fonctions purrr sont de type stable: vous pouvez prédire le type de sortie exclusivement à partir du nom de la fonction. Ce n'est pas vrai pour sapply()
ou mapply()
. Oui, il y a vapply()
; mais il n'y a pas d'équivalent pour mapply()
.
Vous pensez peut-être que toutes ces distinctions mineures ne sont pas importantes (tout comme certaines personnes pensent qu'il n'y a aucun avantage à faire passer les expressions stringr de base R), mais d'après mon expérience, elles causent des frictions inutiles lors de la programmation (les ordres d'arguments différents toujours utilisés me up), et elles rendent les techniques de programmation fonctionnelle plus difficiles à apprendre car, outre les grandes idées, vous devez également apprendre un tas de détails accessoires.
Purrr remplit également certaines variantes de carte utiles qui sont absentes de la base R:
modify()
préserve le type des données en utilisant [[<-
pour modifier "in place". En conjonction avec la variante _if
, cela permet d’obtenir un code (beau comme un IMO) comme modify_if(df, is.factor, as.character)
map2()
vous permet de mapper simultanément sur x
et y
. Cela facilite l’expression d’idées telles que map2(models, datasets, predict)
imap()
vous permet de mapper simultanément sur x
et ses index (noms ou positions). Cela facilite, par exemple, le chargement de tous les fichiers csv
d'un répertoire, en ajoutant une colonne filename
à chacun.
dir("\\.csv$") %>%
set_names() %>%
map(read.csv) %>%
imap(~ transform(.x, filename = .y))
walk()
renvoie son entrée de manière invisible; et est utile lorsque vous appelez une fonction pour ses effets secondaires (c'est-à-dire l'écriture de fichiers sur le disque).
Sans parler des autres aides comme safely()
et partial()
.
Personnellement, je trouve que lorsque j'utilise purrr, je peux écrire du code fonctionnel avec moins de friction et une plus grande facilité. cela réduit l’écart entre la conception d’une idée et sa mise en œuvre. Mais votre kilométrage peut varier. il n’est pas nécessaire d’utiliser purrr sauf s’il vous aide réellement.
Oui, map()
est légèrement plus lent que lapply()
. Mais le coût d'utilisation de map()
ou lapply()
dépend de ce que vous mappez et non de la surcharge de l'exécution de la boucle. Le micro-repère ci-dessous suggère que le coût de map()
par rapport à lapply()
est d'environ 40 ns par élément, ce qui semble peu susceptible d'avoir un impact significatif sur la plupart des codes R.
library(purrr)
n <- 1e4
x <- 1:n
f <- function(x) NULL
mb <- microbenchmark::microbenchmark(
lapply = lapply(x, f),
map = map(x, f)
)
summary(mb, unit = "ns")$median / n
#> [1] 490.343 546.880
purrr
et lapply
revient à commodité et vitesse.purrr::map
_ est syntaxiquement plus pratique que lapplyextraire le deuxième élément de la liste
_map(list, 2)
_
qui comme @F. Privé a souligné, est le même que:
_map(list, function(x) x[[2]])
_
avec lapply
_lapply(list, 2) # doesn't work
_
nous devons passer une fonction anonyme ...
_lapply(list, function(x) x[[2]]) # now it works
_
... ou comme l'a souligné @RichScriven, nous passons _[[
_ comme argument dans lapply
_lapply(list, `[[`, 2) # a bit more simple syntantically
_
Donc, si vous vous retrouvez à appliquer des fonctions à plusieurs listes à l'aide de lapply
, et que vous en avez assez de définir une fonction personnalisée ou d'écrire une fonction anonyme, la commodité est l'une des raisons pour lesquelles vous préférez privilégier purrr
.
map_chr()
map_lgl()
map_int()
map_dbl()
map_df()
- mon préféré, retourne un cadre de données.Chacune de ces fonctions de carte spécifiques à un type renvoie une liste atomique (vecteur), plutôt que les listes renvoyées par map()
et lapply()
. Si vous avez à l'intérieur des listes imbriquées de vecteurs atomiques, vous pouvez utiliser ces fonctions de carte spécifiques au type pour extraire directement les vecteurs et contraindre les vecteurs directement dans les vecteurs int, dbl, chr. La version de base R ressemblerait à quelque chose comme as.numeric(sapply(...))
, as.character(sapply(...))
, etc. Ceci donne purrr
un autre point pour la commodité et la fonctionnalité.
lapply
est [légèrement] plus rapide que map
Utilisation des fonctions pratiques de purrr
, sous la forme @F. Privé a souligné ralentit un peu le traitement. Faisons la course à chacun des 4 cas que j'ai présentés ci-dessus.
_# devtools::install_github("jennybc/repurrrsive")
library(repurrrsive)
library(purrr)
library(microbenchmark)
library(ggplot2)
mbm <- microbenchmark(
lapply = lapply(got_chars[1:4], function(x) x[[2]]),
lapply_2 = lapply(got_chars[1:4], `[[`, 2),
map_shortcut = map(got_chars[1:4], 2),
map = map(got_chars[1:4], function(x) x[[2]]),
times = 100
)
autoplot(mbm)
_
Et le gagnant est....
_lapply(list, `[[`, 2)
_
En résumé, si vous recherchez une vitesse brute: base::lapply
(bien que ce ne soit pas beaucoup plus rapide)
Pour une syntaxe et une expressibilité simples: purrr::map
Cet excellent tutoriel purrr
met en évidence le commodité de ne pas avoir à écrire explicitement les fonctions anonymes lors de l'utilisation de purrr
, et les avantages des types spécifiques map
fonctions.
Si nous ne tenons pas compte des aspects du goût (sinon la question devrait être fermée) ou de la cohérence de la syntaxe, du style, etc., la réponse est non, il n'y a pas de raison particulière d'utiliser map
au lieu de lapply
ou d'autres variantes de la famille à appliquer, telle que la règle plus stricte vapply
.
PS: Pour ceux qui votent gratuitement, rappelez-vous que le PO a écrit:
Je ne pose pas de questions sur ce que l’on aime ou n’aime pas sur la syntaxe, les autres fonctionnalités fournies par purrr, etc., mais strictement sur la comparaison de purrr :: map avec lapply en utilisant l’évaluation standard
Si vous ne tenez pas compte de la syntaxe ni des autres fonctionnalités de purrr
, il n’ya aucune raison particulière d’utiliser map
. J’utilise purrr
moi-même et la réponse de Hadley me convient, mais il va ironiquement sur les choses que le PO a énoncées dès le départ, il ne demandait pas.