Qual è la differenza tra lapply e do.call?

Sto imparando R di recente e confuso da due funzioni: lapply e do.call . Sembra che siano simili alla funzione map in Lisp. Ma perché ci sono due funzioni con un nome così diverso? Perché R non usa solo una funzione chiamata map ?

Esiste una funzione chiamata Map che può essere simile alla mappa in altre lingue:

  • lapply restituisce una lista della stessa lunghezza di X, ciascun elemento del quale è il risultato dell’applicazione di FUN all’elemento corrispondente di X.

  • costruisce do.call ed esegue una chiamata di funzione da un nome o una funzione e un elenco di argomenti da do.call .

  • Map applica una funzione agli elementi corrispondenti di determinati vettori … Map è un semplice wrapper mapply che non tenta di semplificare il risultato, simile alla mapcar del Common Lisp (con gli argomenti comunque riciclati). Le versioni future potrebbero consentire un certo controllo del tipo di risultato.


  1. Map è un wrapper attorno a mapply
  2. lapply è un caso speciale di mapply
  3. Pertanto Map e lapply saranno simili in molti casi.

Ad esempio, qui è lapply :

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

E lo stesso usando la 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 prende una funzione come input e splatter gli altri argomenti alla funzione. È ampiamente usato, per esempio, per assemblare liste in strutture più semplici (spesso con rbind o cbind ).

Per esempio:

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

lapply applica una funzione su un elenco, do.call chiama una funzione con un elenco di argomenti. Mi sembra davvero una differenza …

Per dare un esempio con una lista:

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

Con lappy ottieni la media di ogni elemento nella lista come questo:

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

do.call dà un errore, poiché la media si aspetta che l'argomento "assetto" sia 1.

D'altra parte, rbind lega tutti gli argomenti in fila. Quindi per bind X in fila, fai:

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

Se tu lapply , R applicherebbe rbind a ogni elemento della lista, dandoti questa assurdità:

 > 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 

Per avere qualcosa come Map, è necessario ?mapply , che è qualcosa di completamente diverso. Per ottenere ad esempio la media di ogni elemento in X, ma con un diverso taglio, puoi usare:

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

lapply è simile alla map , do.call non lo è. lapply applica una funzione a tutti gli elementi di una lista, do.call chiama una funzione in cui tutti gli argomenti della funzione sono in una lista. Quindi per un elenco di elementi n , lapply ha n chiamate di funzione e do.call ha solo una chiamata di funzione. Quindi do.call è molto diverso da lapply . Spero che questo chiarisca il tuo problema.

Un esempio di codice:

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

e:

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

In molte parole semplici:

  1. lapply () applica una determinata funzione per ogni elemento in una lista, quindi ci saranno diverse chiamate di funzione.

  2. do.call () applica una determinata funzione alla lista nel suo complesso, quindi c’è solo una chiamata di funzione.

Il modo migliore per imparare è quello di giocare con gli esempi di funzione nella documentazione R.

lapply() è una funzione simile a una mappa. do.call() è diverso. Viene utilizzato per passare gli argomenti a una funzione in forma di elenco anziché averli enumerati. Per esempio,

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

Sebbene ci siano state molte risposte, ecco il mio esempio di riferimento. Supponiamo di avere una lista di dati come:

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

La funzione lapply restituisce una lista.

 lapply(L, sum) 

Quanto sopra significa qualcosa come sotto.

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

Ora facciamo la stessa cosa per do.call

 do.call(sum, L) 

Significa

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

Nel nostro esempio, restituisce 21. In breve, lapply restituisce sempre una lista mentre il tipo restituito da do.call dipende in realtà dalla funzione eseguita.

La differenza tra entrambi sono:

 lapply(1:n,function,parameters) 

=> Invia 1, parametri per funzione => invia 2, parametri per funzionare e così via

 do.call 

Invia semplicemente 1 … n come vettore e parametri per funzionare

Quindi in applicazione hai n chiamate di funzione, in do.call ne hai solo una