r functional-programming

¿Cuál es la diferencia entre lapply y do.call?



functional-programming (8)

Estoy aprendiendo R recientemente y confundido por dos funciones: lapply y do.call . Parece que son simplemente similares a la función de map en Lisp. Pero ¿por qué hay dos funciones con un nombre tan diferente? ¿Por qué R no usa una función llamada map ?


Aunque ha habido muchas respuestas, aquí está mi ejemplo de referencia. Supongamos que tenemos una lista de datos como:

L=list(c(1,2,3), c(4,5,6))

La función lapply devuelve una lista.

lapply(L, sum)

Lo anterior significa algo como a continuación.

list( sum( L[[1]]) , sum( L[[2]]))

Ahora hagamos lo mismo para do.call

do.call(sum, L)

Significa

sum( L[[1]], L[[2]])

En nuestro ejemplo, devuelve 21. En resumen, lapply siempre devuelve una lista, mientras que el tipo de devolución de do.call realmente depende de la función ejecutada.


En la mayoría de las palabras simples:

  1. lapply () aplica una función dada para cada elemento en una lista, por lo que habrá varias llamadas a funciones.

  2. do.call () aplica una función dada a la lista como un todo, por lo que solo hay una llamada a función.

La mejor manera de aprender es jugar con los ejemplos de funciones en la documentación de R.


Hay una función llamada Map que puede ser similar a map en otros idiomas:

  • lapply devuelve una lista de la misma longitud que X, cada elemento de la cual es el resultado de aplicar FUN al elemento correspondiente de X.

  • do.call construye y ejecuta una llamada de función desde un nombre o una función y una lista de argumentos que se le pasarán.

  • Map aplica una función a los elementos correspondientes de vectores dados ... Map es un contenedor simple para mapply que no intenta simplificar el resultado, similar al mapcar de Common Lisp (sin embargo, los argumentos se reciclan). Las versiones futuras pueden permitir cierto control del tipo de resultado.

  1. Map es una envoltura alrededor de mapply
  2. lapply es un caso especial de mapply
  3. Por lo tanto, Map y lapply serán similares en muchos casos.

Por ejemplo, aquí está lapply :

lapply(iris, class) $Sepal.Length [1] "numeric" $Sepal.Width [1] "numeric" $Petal.Length [1] "numeric" $Petal.Width [1] "numeric" $Species [1] "factor"

Y lo mismo usando Map :

Map(class, iris) $Sepal.Length [1] "numeric" $Sepal.Width [1] "numeric" $Petal.Length [1] "numeric" $Petal.Width [1] "numeric" $Species [1] "factor"

do.call toma una función como entrada y salpica sus otros argumentos a la función. Se usa ampliamente, por ejemplo, para ensamblar listas en estructuras más simples (a menudo con rbind o cbind ).

Por ejemplo:

x <- lapply(iris, class) do.call(c, x) Sepal.Length Sepal.Width Petal.Length Petal.Width Species "numeric" "numeric" "numeric" "numeric" "factor"


La diferencia entre ambos es:

lapply(1:n,function,parameters)

=> Este envía 1, parámetros a la función => esto envía 2, parámetros para funcionar y así sucesivamente

do.call

Simplemente envía 1 ... n como vector y parámetros para funcionar

Entonces en aplicar tienes n llamadas a función, en do.call tienes solo una


Sentí que un do.call importante de do.call no se ha demostrado aquí (o no era obvio para mí); es decir, puede pasar parámetros nombrados en una list a una función utilizando do.call .

Por ejemplo, runif toma los parámetros n , min y max . Uno puede pasar estos utilizando do.call como a continuación.

para <- list(n = 10, min = -1, max = 1) do.call(runif, para) #[1] -0.4689827 -0.2557522 0.1457067 0.8164156 -0.5966361 0.7967794 #[7] 0.8893505 0.3215956 0.2582281 -0.8764275


lapply() es una función similar a un mapa. do.call() es diferente. Se usa para pasar los argumentos a una función en forma de lista en lugar de tenerlos enumerados. Por ejemplo,

> do.call("+",list(4,5)) [1] 9


lapply aplica una función sobre una lista, do.call llama a una función con una lista de argumentos. Eso me parece una gran diferencia ...

Para dar un ejemplo con una lista:

X <- list(1:3,4:6,7:9)

Con lapply obtienes la media de cada elemento de la lista como este:

> lapply(X,mean) [[1]] [1] 2 [[2]] [1] 5 [[3]] [1] 8

do.call da un error, ya que mean espera que el argumento "trim" sea 1.

Por otro lado, rbind une todos los argumentos rowwise. Entonces, para unir X rowwise, haces:

> do.call(rbind,X) [,1] [,2] [,3] [1,] 1 2 3 [2,] 4 5 6 [3,] 7 8 9

Si usa lapply , R aplicaría rbind a cada elemento de la lista, dándole este sinsentido:

> lapply(X,rbind) [[1]] [,1] [,2] [,3] [1,] 1 2 3 [[2]] [,1] [,2] [,3] [1,] 4 5 6 [[3]] [,1] [,2] [,3] [1,] 7 8 9

Para tener algo como Map, necesitas ?mapply , que es algo completamente diferente. Para obtener, por ejemplo, la media de cada elemento en X, pero con un recorte diferente, puede usar:

> mapply(mean,X,trim=c(0,0.5,0.1)) [1] 2 5 8


lapply es similar a map , do.call is not. lapply aplica una función a todos los elementos de una lista, do.call llama a una función donde todos los argumentos de función están en una lista. Por lo tanto, para una lista de elementos n , lapply tiene n llamadas de función, y do.call tiene una sola llamada de función. Entonces do.call es bastante diferente de lapply . Espero que esto aclare tu problema.

Un ejemplo de código:

do.call(sum, list(c(1, 2, 4, 1, 2), na.rm = TRUE))

y:

lapply(c(1, 2, 4, 1, 2), function(x) x + 1)