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>)
le résultat devrait être le même et les points de repère que j'ai faits semblent montrer que lapply
c'est légèrement plus rapide (cela devrait être le cas map
pour évaluer toutes les entrées d'évaluation non standard).
Alors, y a-t-il une raison pour laquelle, pour des cas aussi simples, je devrais envisager de passer purrr::map
? Je ne demande pas ici 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 purrr::map
avec l' lapply
utilisation de l'évaluation standard, c'est-à-dire map(<list-like-object>, function(x) <do stuff>)
. Y a-t-il un avantage 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 élaborer un peu plus?
tidyverse
cependant, vous pouvez bénéficier de la syntaxe du tube%>%
et des fonctions anonymes~ .x + 1
~{}
raccourci lambda (avec ou sans les{}
sceaux, l'affaire pour moi pour le simplepurrr::map()
. L'application du typepurrr::map_…()
est pratique et moins obtuse quevapply()
.purrr::map_df()
est une fonction super chère mais elle simplifie aussi le code. Il n'y a absolument rien de mal à s'en tenir à la base R[lsv]apply()
, bien que .purrr
. Mon point est le suivant:tidyverse
est fabuleux pour les analyses / interactifs / rapports, pas pour la programmation. Si vous devez utiliserlapply
oumap
alors vous programmez et pouvez finir un jour par créer un package. Ensuite, les moins dépendances sont les meilleures. Plus: je vois parfois des gens utilisermap
avec une syntaxe assez obscure après. Et maintenant que je vois des tests de performances: si vous êtes habitué à laapply
famille: tenez-vous-y.Réponses:
Si la seule fonction que vous utilisez de purrr est
map()
, alors non, les avantages ne sont pas substantiels. Comme le souligne Rich Pauloo, le principal avantage demap()
est les helpers qui vous permettent d'écrire du code compact pour les cas particuliers courants:~ . + 1
est équivalent àfunction(x) x + 1
list("x", 1)
équivaut àfunction(x) x[["x"]][[1]]
. Ces aides sont un peu plus générales que[[
- voir?pluck
pour plus de détails. Pour les rectangles de données , l'.default
argument est particulièrement utile.Mais la plupart du temps, vous n'utilisez pas une seule fonction
*apply()
/map()
, vous en utilisez un certain nombre, et l'avantage de purrr est une plus grande cohérence entre les fonctions. Par exemple:Le premier argument
lapply()
est les données; le premier argument demapply()
est la fonction. Le premier argument de toutes les fonctions cartographiques est toujours les données.Avec
vapply()
,sapply()
etmapply()
vous pouvez choisir de noms Suppress sur la sortie avecUSE.NAMES = FALSE
; maislapply()
n'a pas cet argument.Il n'existe aucun moyen cohérent de transmettre des arguments cohérents à la fonction mappeur. La plupart des fonctions utilisent
...
maismapply()
utiliseMoreArgs
(ce que vous vous attendez à être appeléMORE.ARGS
) etMap()
,Filter()
etReduce()
s'attendent à ce que vous créiez une nouvelle fonction anonyme. Dans les fonctions de carte, l'argument constant vient 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()
oumapply()
. Oui, il y en avapply()
; mais il n'y a pas d'équivalent pourmapply()
.Vous pouvez penser que toutes ces distinctions mineures ne sont pas importantes (tout comme certaines personnes pensent qu'il n'y a aucun avantage à stringr par rapport aux expressions régulières de base R), mais d'après mon expérience, elles provoquent des frictions inutiles lors de la programmation (les différents ordres d'argumentation sont toujours utilisés pour trébucher me up), et ils rendent les techniques de programmation fonctionnelle plus difficiles à apprendre car en plus des grandes idées, vous devez également apprendre un tas de détails accessoires.
Purrr remplit également certaines variantes de carte pratiques qui sont absentes de la base R:
modify()
préserve le type des données en utilisant[[<-
pour modifier "en place". En conjonction avec la_if
variante, cela permet un code (IMO beautiful) commemodify_if(df, is.factor, as.character)
map2()
vous permet de mapper simultanément surx
ety
. Cela facilite l'expression d'idées telles quemap2(models, datasets, predict)
imap()
vous permet de mapper simultanément surx
et ses indices (noms ou positions). Cela facilite (par exemple) le chargement de tous lescsv
fichiers dans un répertoire, en ajoutant unefilename
colonne à chacun.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()
etpartial()
.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 à moins que cela ne vous aide réellement.
Microbenchmarks
Oui,
map()
est légèrement plus lent quelapply()
. Mais le coût d'utilisationmap()
oulapply()
est déterminé par ce que vous mappez, et non par les frais généraux liés à l'exécution de la boucle. Le microbenchmark ci-dessous suggère que le coût demap()
par rapport àlapply()
est d'environ 40 ns par élément, ce qui semble peu susceptible d'avoir un impact significatif sur la plupart du code R.la source
mutate()
, je voulais juste un exemple simple sans autres déps.map_*
est ce qui m'a permis de chargerpurrr
de nombreux scripts. Cela m'a aidé avec certains aspects de «flux de contrôle» de mon code (stopifnot(is.data.frame(x))
).Comparer
purrr
etlapply
se résume à la commodité et à la vitesse .1.
purrr::map
est syntaxiquement plus pratique que lapplyextraire le deuxième élément de la liste
qui comme @F. Privé a souligné, c'est la même chose que:
avec
lapply
nous devons passer une fonction anonyme ...
... ou comme l'a souligné @RichScriven, nous passons
[[
comme argument danslapply
Donc, si vous vous retrouvez à appliquer des fonctions à de nombreuses listes en utilisant
lapply
, et que vous vous lassez de définir une fonction personnalisée ou d'écrire une fonction anonyme, la commodité est une raison à privilégierpurrr
.2. Les fonctions de carte spécifiques au type sont simplement de nombreuses lignes de code
map_chr()
map_lgl()
map_int()
map_dbl()
map_df()
Chacune de ces fonctions de mappage spécifiques au type renvoie un vecteur, plutôt que les listes renvoyées par
map()
etlapply()
. Si vous avez affaire à des listes imbriquées de vecteurs, vous pouvez utiliser ces fonctions de carte spécifiques au type pour extraire les vecteurs directement et les contraindre directement en vecteurs int, dbl, chr. La version de base R ressemblerait à quelque chose commeas.numeric(sapply(...))
,as.character(sapply(...))
, etc.Les
map_<type>
fonctions ont également la qualité utile que si elles ne peuvent pas renvoyer un vecteur atomique du type indiqué, elles échouent. Ceci est utile lors de la définition d'un flux de contrôle strict, où vous voulez qu'une fonction échoue si elle génère [d'une manière ou d'une autre] le mauvais type d'objet.3. Mis à part la commodité,
lapply
est [légèrement] plus rapide quemap
Utilisation
purrr
des fonctions pratiques de, comme @F. Privé a souligné ralentit un peu le traitement. Faisons la course pour chacun des 4 cas que j'ai présentés ci-dessus.Et le gagnant est....
En résumé, si la vitesse brute est ce que vous recherchez:
base::lapply
(même si ce n'est pas beaucoup plus rapide)Pour une syntaxe et une expressibilité simples:
purrr::map
Cet excellent
purrr
tutoriel met en évidence la commodité de ne pas avoir à écrire explicitement des fonctions anonymes lors de l'utilisationpurrr
, et les avantages desmap
fonctions spécifiques au type .la source
function(x) x[[2]]
au lieu de juste2
, ce serait moins lent. Tout ce temps supplémentaire est dû à des contrôles quilapply
ne fonctionnent pas.[[
est une fonction. Vous pouvez le fairelapply(list, "[[", 3)
.Si nous ne considérons pas les aspects de goût (sinon cette question devrait être fermée) ou la cohérence de la syntaxe, le style, etc., la réponse est non, il n'y a pas de raison particulière d'utiliser
map
placelapply
ou d'autres variantes de la famille apply, telles que la plus strictevapply
.PS: À ces personnes qui votent gratuitement, rappelez-vous simplement que le PO a écrit:
Si vous ne tenez pas compte de la syntaxe ou d'autres fonctionnalités de
purrr
, il n'y a aucune raison particulière à utilisermap
. Je m'utilisepurrr
moi-même et je suis d'accord avec la réponse de Hadley, mais ironiquement, cela revient sur les choses mêmes que l'OP a déclaré d'emblée qu'il ne demandait pas.la source