Qual é a diferença entre lapply e do.call?

143

Estou aprendendo R recentemente e confuso com duas funções: lapplye do.call. Parece que eles são similares à mapfunção no Lisp. Mas por que existem duas funções com um nome tão diferente? Por que R não usa apenas uma função chamada map?

Hanfei Sun
fonte

Respostas:

125

Há uma função chamada Mapque pode ser semelhante ao mapa em outros idiomas:

  • lapply retorna uma lista do mesmo tamanho que X, cada elemento resultante da aplicação de FUN ao elemento correspondente de X.

  • do.call constrói e executa uma chamada de função a partir de um nome ou uma função e uma lista de argumentos a serem passados ​​para ele.

  • Mapaplica uma função aos elementos correspondentes de determinados vetores ... Mapé um invólucro simples para o mapplyqual não tenta simplificar o resultado, semelhante ao mapcar do Common Lisp (com argumentos sendo reciclados, no entanto). Versões futuras podem permitir algum controle do tipo de resultado.


  1. Map é um invólucro mapply
  2. lapply é um caso especial de mapply
  3. Portanto Mape lapplyserá semelhante em muitos casos.

Por exemplo, aqui está lapply:

lapply(iris, class)
$Sepal.Length
[1] "numeric"

$Sepal.Width
[1] "numeric"

$Petal.Length
[1] "numeric"

$Petal.Width
[1] "numeric"

$Species
[1] "factor"

E o mesmo 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.callrecebe uma função como entrada e espalha seus outros argumentos para a função. É amplamente utilizado, por exemplo, para montar listas em estruturas mais simples (geralmente com rbindou cbind).

Por exemplo:

x <- lapply(iris, class)
do.call(c, x)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
   "numeric"    "numeric"    "numeric"    "numeric"     "factor" 
Andrie
fonte
4
na verdade eu encontrei do.callé quase o mesmo que applyem Lisp
Hanfei Sun
não é esse último exemplo suposto ser do.call(cbind, x)a versão atual me dá Error in do.call(c, x) : 'what' must be a function or character string...
sindri_baldur
1
@snoram Esse exemplo ainda funciona. A função cbind()é diferente da função c()e, embora isso também funcione, fornece resultados diferentes.
Andrie
61

lapplyaplica uma função sobre uma lista, do.callchama uma função com uma lista de argumentos. Isso parece uma grande diferença para mim ...

Para dar um exemplo com uma lista:

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

Com lapply, você obtém a média de cada elemento da lista como este:

> lapply(X,mean)
[[1]]
[1] 2

[[2]]
[1] 5

[[3]]
[1] 8

do.call gera um erro, pois a média espera que o argumento "trim" seja 1.

Por outro lado, rbindliga todos os argumentos na linha. Então, para ligar o X rowwise, você deve:

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

Se você usasse lapply, R se aplicaria rbinda todos os elementos da lista, dando a você essa bobagem:

> 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 ter algo como Map, você precisa ?mapply, que é algo totalmente diferente. Para obter, por exemplo, a média de cada elemento em X, mas com um corte diferente, você pode usar:

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

lapplyé semelhante a map, do.callnão é. lapplyaplica uma função a todos os elementos de uma lista, do.callchama uma função em que todos os argumentos da função estão em uma lista. Portanto, para uma nlista de elementos, lapplypossui nchamadas de função e do.callpossui apenas uma chamada de função. Então, do.callé bem diferente de lapply. Espero que isso esclareça seu problema.

Um exemplo de código:

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)
Paul Hiemstra
fonte
25

Nas palavras mais simples:

  1. lapply () aplica uma determinada função para cada elemento em uma lista, portanto haverá várias chamadas de função.

  2. do.call () aplica uma determinada função à lista como um todo, portanto, há apenas uma chamada de função.

A melhor maneira de aprender é brincar com os exemplos de funções na documentação do R.

LoveMeow
fonte
12

lapply()é uma função parecida com um mapa. do.call()é diferente. É usado para passar os argumentos para uma função em forma de lista em vez de tê-los enumerados. Por exemplo,

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

Embora tenha havido muitas respostas, aqui está o meu exemplo para referência. Suponha que tenhamos uma lista de dados como:

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

A função lapply retorna uma lista.

lapply(L, sum) 

O acima significa algo como abaixo.

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

Agora vamos fazer a mesma coisa para do.call

do.call(sum, L) 

Isso significa

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

No nosso exemplo, ele retorna 21. Em resumo, lapply sempre retorna uma lista, enquanto o tipo de retorno do.call realmente depende da função executada.

kimman
fonte
5

A diferença entre ambos são:

lapply(1:n,function,parameters)

=> Envia 1, parâmetros para funcionar => envia 2, parâmetros para funcionar e assim por diante

do.call 

Apenas envia 1… n como vetor e parâmetros para funcionar

Portanto, em apply você tem n chamadas de função, em do.call você tem apenas um

nitin lal
fonte