2012-09-09 41 views
68

Các tài liệu nóiTại sao `vapply` an toàn hơn` sapply`?

vapply cũng tương tự như sapply, nhưng có một kiểu xác định trước của giá trị trả về, vì vậy nó có thể được an toàn hơn [...] để sử dụng.

Bạn có thể giải thích tại sao nó thường an toàn hơn, có thể là ví dụ?


P .: Tôi biết câu trả lời và tôi đã có xu hướng tránh sapply. Tôi chỉ muốn có một câu trả lời hay ở đây trên SO vì vậy tôi có thể chỉ cho đồng nghiệp của tôi với nó. Xin vui lòng, không có câu trả lời "đọc hướng dẫn".

+1

Có thể dự đoán được nhiều hơn, làm cho mã ít mơ hồ hơn và mạnh mẽ hơn. Đặc biệt trong các dự án lớn hơn, nói một gói lớn, điều này là có liên quan. –

+3

Không cần “P.S.”… chỉ cần tự trả lời câu hỏi. –

+5

@KonradRudolph Đôi khi các lưu ý về hiệu ứng đó có thể tập trung vào câu hỏi và tránh các câu trả lời "RTFM". :-) –

Trả lời

58

Như đã được ghi nhận, vapply thực hiện hai điều:

  • cải thiện tốc độ nhẹ
  • Cải thiện tính nhất quán bằng cách cung cấp kiểm tra kiểu trả về hạn chế.

Điểm thứ hai là lợi thế lớn hơn, vì nó giúp bắt lỗi trước khi chúng xảy ra và dẫn đến mã mạnh hơn. Việc kiểm tra giá trị trả lại này có thể được thực hiện riêng bằng cách sử dụng sapply theo sau là stopifnot để đảm bảo rằng giá trị trả về phù hợp với những gì bạn mong đợi, nhưng vapply thì dễ dàng hơn một chút (nếu bị giới hạn hơn, vì mã kiểm tra lỗi tùy chỉnh có thể kiểm tra các giá trị trong giới hạn , v.v.)

Dưới đây là ví dụ về số vapply đảm bảo kết quả của bạn như mong đợi. Điều này tương tự với điều tôi đang làm trong khi đang cạo PDF, trong đó findD sẽ sử dụng để khớp mẫu trong dữ liệu thô (ví dụ: tôi có danh sách là split theo thực thể và regex để khớp với địa chỉ trong mỗi thực thể. Thỉnh thoảng, PDF đã được chuyển đổi không đúng thứ tự và sẽ có hai địa chỉ cho một thực thể, điều này gây ra sự xấu).

> input1 <- list(letters[1:5], letters[3:12], letters[c(5,2,4,7,1)]) 
> input2 <- list(letters[1:5], letters[3:12], letters[c(2,5,4,7,15,4)]) 
> findD <- function(x) x[x=="d"] 
> sapply(input1, findD) 
[1] "d" "d" "d" 
> sapply(input2, findD) 
[[1]] 
[1] "d" 

[[2]] 
[1] "d" 

[[3]] 
[1] "d" "d" 

> vapply(input1, findD, "") 
[1] "d" "d" "d" 
> vapply(input2, findD, "") 
Error in vapply(input2, findD, "") : values must be length 1, 
but FUN(X[[3]]) result is length 2 

Khi tôi nói với sinh viên, một phần trở thành lập trình viên đang thay đổi suy nghĩ của bạn từ "lỗi gây khó chịu" thành "lỗi là bạn của tôi".

Zero, chiều dài đầu vào
Một điểm liên quan là nếu độ dài đầu vào là zero, sapply sẽ luôn trả về một danh sách rỗng, không phụ thuộc vào loại đầu vào. Hãy so sánh:

sapply(1:5, identity) 
## [1] 1 2 3 4 5 
sapply(integer(), identity) 
## list()  
vapply(1:5, identity) 
## [1] 1 2 3 4 5 
vapply(integer(), identity) 
## integer(0) 

Với vapply, bạn được đảm bảo để có một loại đặc biệt của đầu ra, do đó bạn không cần phải viết kiểm tra thêm cho zero đầu vào chiều dài.

Benchmarks

vapply có thể nhanh hơn một chút vì nó đã biết những gì định dạng nó nên được mong đợi các kết quả trong.

input1.long <- rep(input1,10000) 

library(microbenchmark) 
m <- microbenchmark(
    sapply(input1.long, findD), 
    vapply(input1.long, findD, "") 
) 
library(ggplot2) 
library(taRifx) # autoplot.microbenchmark is moving to the microbenchmark package in the next release so this should be unnecessary soon 
autoplot(m) 

autoplot

12

Nếu bạn luôn muốn kết quả của mình trở thành một cái gì đó đặc biệt ... ví dụ: một vector hợp lý. vapply đảm bảo điều này xảy ra nhưng sapply không nhất thiết phải làm như vậy.

a<-vapply(NULL, is.factor, FUN.VALUE=logical(1)) 
b<-sapply(NULL, is.factor) 

is.logical(a) 
is.logical(b) 
+3

tôi nghĩ rằng điều rõ ràng nhất cần làm là 'logic (1)' trong trường hợp này, như FALSE trông giống như nó đặt một tùy chọn để "OFF" thay vì chỉ định một loại –

13

Các nét chính phụ có liên quan với vapply có thể giúp bạn tiết kiệm thời gian gỡ lỗi kết quả khó hiểu sau. Nếu hàm bạn đang gọi có thể trả về các kiểu dữ liệu khác nhau, thì chắc chắn phải sử dụng vapply.

Một ví dụ mà bạn nghĩ đến là sqlQuery trong gói RODBC. Nếu có lỗi khi thực thi truy vấn, hàm này trả về một vector character với thông báo. Vì vậy, ví dụ, nói rằng bạn đang cố gắng để lặp qua một vector của các tên bảng tnames và chọn giá trị tối đa từ cột số 'NumCol' trong mỗi bảng với:

sapply(tnames, 
    function(tname) sqlQuery(cnxn, paste("SELECT MAX(NumCol) FROM", tname))[[1]]) 

Nếu tất cả các tên bảng có giá trị , điều này sẽ dẫn đến một véc tơ numeric. Nhưng nếu một trong các tên bảng xảy ra để thay đổi trong cơ sở dữ liệu và truy vấn thất bại, kết quả sẽ bị ép buộc vào chế độ character. Tuy nhiên, việc sử dụng vapply với FUN.VALUE=numeric(1) sẽ dừng lỗi ở đây và ngăn không cho nó xuất hiện ở đâu đó dưới dòng --- hoặc tệ hơn, hoàn toàn không xảy ra.

Các vấn đề liên quan