¿Hay alguna razón por la que debería usar
map(<list-like-object>, function(x) <do stuff>)
en vez de
lapply(<list-like-object>, function(x) <do stuff>)
el resultado debería ser el mismo y los puntos de referencia que realicé parecen mostrar que lapplyes un poco más rápido (debería ser como mapevaluar todas las entradas de evaluación no estándar).
Entonces, ¿hay alguna razón por la cual para casos tan simples debería considerar cambiarme purrr::map? No estoy pidiendo aquí sobre gustos o disgustos sobre la sintaxis de uno, otras funcionalidades proporcionadas por purrr etc., pero estrictamente sobre la comparación de purrr::mapla lapplysuponiendo el uso de la evaluación estándar, es decir map(<list-like-object>, function(x) <do stuff>). ¿Hay alguna ventaja purrr::mapen términos de rendimiento, manejo de excepciones, etc.? Los comentarios a continuación sugieren que no es así, pero ¿tal vez alguien podría elaborar un poco más?

tidyverse, puede beneficiarse de la sintaxis de canalización%>%y funciones anónimas~ .x + 1~{}lambda acceso directo (con o sin los{}sellos de la oferta para mí por llanopurrr::map(). El tipo de aplicación de lapurrr::map_…()son muy útiles y menos obtusos quevapply().purrr::map_df()es una función caro súper pero también simplifica código. No hay absolutamente nada de malo en la pervivencia de la base R[lsv]apply(), aunque .purrrcosas. Mi punto es el siguiente:tidyversees fabuloso para análisis / interactivo / informes, no para programación. Si tiene que usarlapplyomapestá programando y puede terminar algún día creando un paquete. Entonces, cuanto menos dependencias, mejor. Además: a veces veo personas que usanmapuna sintaxis bastante oscura después. Y ahora que veo pruebas de rendimiento: si estás acostumbrado a laapplyfamilia: mantente firme.Respuestas:
Si la única función que está utilizando de purrr es
map(), entonces no, las ventajas no son sustanciales. Como señala Rich Pauloo, la principal ventaja demap()los ayudantes es que le permiten escribir código compacto para casos especiales comunes:~ . + 1es equivalente afunction(x) x + 1list("x", 1)es equivalente afunction(x) x[["x"]][[1]]. Estos ayudantes son un poco más generales que[[- ver?pluckpara más detalles. Para el rectángulo de datos , el.defaultargumento es particularmente útil.Pero la mayoría de las veces no estás usando una sola función
*apply()/map(), estás usando un montón de ellas, y la ventaja de ronronear es una consistencia mucho mayor entre las funciones. Por ejemplo:El primer argumento para
lapply()es los datos; El primer argumentomapply()es la función. El primer argumento para todas las funciones del mapa son siempre los datos.Con
vapply(),,sapply()ymapply()puede optar por suprimir nombres en la salida conUSE.NAMES = FALSE; perolapply()no tiene ese argumento.No hay una forma consistente de pasar argumentos consistentes a la función del mapeador. La mayoría de las funciones usan
...peromapply()usanMoreArgs(que esperaría que se llamaraMORE.ARGS), yMap(),Filter()yReduce()esperan que cree una nueva función anónima. En las funciones de mapa, el argumento constante siempre viene después del nombre de la función.Casi todas las funciones de purrr son de tipo estable: puede predecir el tipo de salida exclusivamente a partir del nombre de la función. Esto no es cierto para
sapply()omapply(). Sí lo hayvapply(); pero no hay equivalente paramapply().Puede pensar que todas estas distinciones menores no son importantes (al igual que algunas personas piensan que no hay ninguna ventaja en encadenar sobre las expresiones regulares de base R), pero en mi experiencia causan fricciones innecesarias al programar (las diferentes órdenes de argumento siempre solían dispararse Me up), y hacen que las técnicas de programación funcional sean más difíciles de aprender porque además de las grandes ideas, también tienes que aprender un montón de detalles incidentales.
Purrr también completa algunas variantes de mapas útiles que están ausentes de la base R:
modify()preserva el tipo de los datos utilizando[[<-para modificar "en el lugar". En combinación con la_ifvariante, esto permite un código (IMO hermoso) comomodify_if(df, is.factor, as.character)map2()le permite mapear simultáneamente sobrexyy. Esto hace que sea más fácil expresar ideas comomap2(models, datasets, predict)imap()le permite mapear simultáneamentexy sus índices (ya sea nombres o posiciones). Esto facilita (por ejemplo) cargar todos loscsvarchivos en un directorio, agregando unafilenamecolumna a cada uno.walk()devuelve su entrada de forma invisible; y es útil cuando se llama a una función por sus efectos secundarios (es decir, escribir archivos en el disco).Sin mencionar los otros ayudantes como
safely()ypartial().Personalmente, encuentro que cuando uso purrr, puedo escribir código funcional con menos fricción y mayor facilidad; disminuye la brecha entre pensar una idea e implementarla. Pero tu kilometraje puede variar; no hay necesidad de usar ronroneo a menos que realmente te ayude.
Microbenchmarks
Sí,
map()es un poco más lento quelapply(). Sin embargo, el coste de utilizaciónmap()olapply()es impulsado por lo que se aplica la relación, no la carga de realizar el bucle. El microbench de abajo sugiere que el costo demap()comparaciónlapply()es de alrededor de 40 ns por elemento, lo que parece poco probable que afecte materialmente a la mayoría del código R.fuente
mutate(), solo quería un ejemplo simple sin otros deps.map_*es lo que me hizo cargarpurrren muchos scripts. Me ayudó con algunos aspectos de 'flujo de control' de mi código (stopifnot(is.data.frame(x))).Comparando
purrry selapplyreduce a conveniencia y velocidad .1.
purrr::mapes sintácticamente más conveniente que lapplyextraer el segundo elemento de la lista
que como @F. Privé señaló, es lo mismo que:
con
lapplynecesitamos pasar una función anónima ...
... o como señaló @RichScriven, pasamos
[[como argumento alapplyEntonces, si se encuentra aplicando funciones a muchas listas usando
lapply, y se cansa de definir una función personalizada o escribir una función anónima, la conveniencia es una de las razones a favorpurrr.2. El mapa específico del tipo funciona simplemente con muchas líneas de código
map_chr()map_lgl()map_int()map_dbl()map_df()Cada una de estas funciones de mapa específicas de tipo devuelve un vector, en lugar de las listas devueltas por
map()ylapply(). Si se trata de listas anidadas de vectores, puede usar estas funciones de mapa específicas de tipo para extraer los vectores directamente y coaccionar los vectores directamente en vectores int, dbl, chr. La versión de la base R sería algo comoas.numeric(sapply(...)),as.character(sapply(...)), etc.Las
map_<type>funciones también tienen la calidad útil de que si no pueden devolver un vector atómico del tipo indicado, fallarán. Esto es útil al definir un flujo de control estricto, donde desea que una función falle si [de alguna manera] genera el tipo de objeto incorrecto.3. Conveniencia aparte,
lapplyes [ligeramente] más rápido quemapUtilizando
purrrlas funciones de conveniencia, como @F. Privé señaló que ralentiza un poco el procesamiento. Vamos a competir con cada uno de los 4 casos que presenté anteriormente.Y el ganador es....
En resumen, si la velocidad bruta es lo que buscas:
base::lapply(aunque no es mucho más rápido)Para sintaxis simple y expresibilidad:
purrr::mapEste excelente
purrrtutorial resalta la conveniencia de no tener que escribir explícitamente funciones anónimas cuando se usapurrr, y los beneficios de lasmapfunciones específicas de tipo .fuente
function(x) x[[2]]lugar de solo2, sería menos lento. Todo este tiempo extra se debe a los controles quelapplyno funcionan.[[Es una función. Puedes hacerlapply(list, "[[", 3).Si no consideramos aspectos del gusto (de lo contrario, esta pregunta debería cerrarse) o la consistencia de la sintaxis, el estilo, etc., la respuesta es no, no hay una razón especial para usar en
maplugar delapplyotras variantes de la familia de aplicación, como la más estrictavapply.PD: Para aquellas personas que votan negativamente, solo recuerden que el OP escribió:
Si no considera la sintaxis ni otras funcionalidades de
purrr, no hay razón especial para usarmap. Me usopurrry estoy de acuerdo con la respuesta de Hadley, pero irónicamente repasa las mismas cosas que el OP declaró por adelantado que no estaba preguntando.fuente