Tôi đang học R gần đây và bối rối bởi hai chức năng: lapply
và do.call
. Có vẻ như chúng chỉ tương tự như map
chức năng trong Lisp. Nhưng tại sao có hai chức năng với một tên khác nhau như vậy? Tại sao R không sử dụng hàm gọi là map
?
Tôi đang học R gần đây và bối rối bởi hai chức năng: lapply
và do.call
. Có vẻ như chúng chỉ tương tự như map
chức năng trong Lisp. Nhưng tại sao có hai chức năng với một tên khác nhau như vậy? Tại sao R không sử dụng hàm gọi là map
?
Câu trả lời:
Có một chức năng được gọi là Map
có thể tương tự như bản đồ trong các ngôn ngữ khác:
lapply
trả về một danh sách có cùng độ dài với X, mỗi phần tử là kết quả của việc áp dụng FUN cho phần tử tương ứng của X.
do.call
xây dựng và thực hiện một cuộc gọi hàm từ một tên hoặc một hàm và một danh sách các đối số được truyền cho nó.
Map
áp dụng một hàm cho các phần tử tương ứng của các vectơ đã cho ... Map
là một trình bao bọc đơn giản mapply
mà không cố gắng đơn giản hóa kết quả, tương tự như bản đồ của Common Lisp (tuy nhiên với các đối số được tái chế). Các phiên bản trong tương lai có thể cho phép một số kiểm soát của loại kết quả.
Map
là một bao bọc xung quanh mapply
lapply
là một trường hợp đặc biệt của mapply
Map
và lapply
sẽ tương tự trong nhiều trường hợp.Ví dụ, đây là lapply
:
lapply(iris, class)
$Sepal.Length
[1] "numeric"
$Sepal.Width
[1] "numeric"
$Petal.Length
[1] "numeric"
$Petal.Width
[1] "numeric"
$Species
[1] "factor"
Và sử dụng tương tự 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
nhận một hàm làm đầu vào và phân tách các đối số khác của nó cho hàm. Nó được sử dụng rộng rãi, ví dụ, để tập hợp các danh sách thành các cấu trúc đơn giản hơn (thường có rbind
hoặc cbind
).
Ví dụ:
x <- lapply(iris, class)
do.call(c, x)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
"numeric" "numeric" "numeric" "numeric" "factor"
do.call(cbind, x)
phiên bản hiện tại mang lại cho tôi Error in do.call(c, x) : 'what' must be a function or character string
...
cbind()
này khác với hàm c()
và mặc dù điều này cũng hoạt động, nhưng nó cho kết quả khác nhau.
lapply
áp dụng một hàm trên một danh sách, do.call
gọi một hàm với một danh sách các đối số. Điều đó có vẻ khá khác biệt với tôi ...
Để đưa ra một ví dụ với một danh sách:
X <- list(1:3,4:6,7:9)
Với lapply bạn có được ý nghĩa của mọi yếu tố trong danh sách như thế này:
> lapply(X,mean)
[[1]]
[1] 2
[[2]]
[1] 5
[[3]]
[1] 8
do.call
đưa ra một lỗi, như có nghĩa là mong đợi đối số "trim" là 1.
Mặt khác, rbind
liên kết tất cả các đối số theo hàng. Vì vậy, để liên kết X theo hàng, bạn làm:
> do.call(rbind,X)
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 4 5 6
[3,] 7 8 9
Nếu bạn sẽ sử dụng lapply
, R sẽ áp dụng rbind
cho mọi thành phần của danh sách, cung cấp cho bạn điều vô nghĩa này:
> 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
Để có một cái gì đó giống như Bản đồ, bạn cần ?mapply
, đó là một cái gì đó khác nhau hoàn toàn. Để lấy ví dụ: giá trị trung bình của mọi phần tử trong X, nhưng với cách cắt khác nhau, bạn có thể sử dụng:
> mapply(mean,X,trim=c(0,0.5,0.1))
[1] 2 5 8
lapply
là tương tự map
, do.call
là không. lapply
áp dụng một hàm cho tất cả các thành phần của danh sách, do.call
gọi một hàm trong đó tất cả các đối số hàm nằm trong danh sách. Vì vậy, đối với một n
danh sách thành phần, lapply
có n
các lệnh gọi hàm và do.call
chỉ có một lệnh gọi hàm. Vì vậy, do.call
là khá khác nhau từ lapply
. Hy vọng điều này làm rõ vấn đề của bạn.
Một ví dụ mã:
do.call(sum, list(c(1, 2, 4, 1, 2), na.rm = TRUE))
và:
lapply(c(1, 2, 4, 1, 2), function(x) x + 1)
Nói một cách đơn giản nhất:
lapply () áp dụng một hàm nhất định cho từng thành phần trong danh sách, do đó sẽ có một số lệnh gọi hàm.
do.call () áp dụng một hàm nhất định cho toàn bộ danh sách, do đó chỉ có một lệnh gọi hàm.
Cách tốt nhất để học là chơi xung quanh với các ví dụ chức năng trong tài liệu R.
Mặc dù đã có nhiều câu trả lời, đây là ví dụ của tôi để tham khảo. Giả sử chúng ta có một danh sách dữ liệu như:
L=list(c(1,2,3), c(4,5,6))
Hàm lapply trả về một danh sách.
lapply(L, sum)
Ở trên có nghĩa là một cái gì đó như dưới đây.
list( sum( L[[1]]) , sum( L[[2]]))
Bây giờ hãy để chúng tôi làm điều tương tự cho do.call
do.call(sum, L)
Nó có nghĩa là
sum( L[[1]], L[[2]])
Trong ví dụ của chúng tôi, nó trả về 21. Tóm lại, lapply luôn trả về một danh sách trong khi kiểu trả về của do.call thực sự phụ thuộc vào hàm được thực thi.
Sự khác biệt giữa cả hai là:
lapply(1:n,function,parameters)
=> Điều này gửi 1, tham số cho hàm => điều này sẽ gửi 2, tham số cho chức năng, v.v.
do.call
Chỉ cần gửi 1 Nv như một vectơ và tham số cho hàm
Vì vậy, trong ứng dụng bạn có n cuộc gọi chức năng, trong do.call bạn chỉ có một cuộc gọi
do.call
gần giống nhưapply
ở Lisp