2012-09-09 59 views
68

文檔說爲什麼vapply比sapply更安全?

vapply類似於sapply,但有一個預先指定的返回值的類型,所以可以更安全[...]使用。

請您詳細說明爲什麼它通常更安全,也許提供示例?


P.S .:我知道答案,我已經傾向於避免sapply。我只希望在這裏有一個很好的答案,所以我可以指出我的同事。請不要「閱讀手冊」的答案。

+1

它更具可預測性,使得代碼更模糊,更健壯。特別是在大型項目中,比如說一個大型包裝,這是相關的。 –

+3

不需要「P.S.」......只是自問這個問題。 –

+5

@KonradRudolph有時候這種效果的註釋可以集中問題並避免「RTFM」答案。 :-) –

回答

58

正如已經指出的那樣,vapply做了兩兩件事:

  • 略有提高速度
  • 通過提供有限的返回類型檢查,提高了一致性。

第二點是更大的優勢,因爲它有助於在錯誤發生之前捕捉錯誤並導致更健壯的代碼。這個返回值檢查可以通過使用sapply然後stopifnot以確保返回值與您的預期一致來單獨完成,但vapply更容易一些(如果更有限,由於自定義錯誤檢查代碼可以檢查邊界內的值等)。

下面是vapply的一個示例,確保您的結果符合預期。這與我剛剛在PDF抓取時正在進行的工作相似,其中findD將使用來匹配原始文本數據中的模式(例如,我將按實體列出一個爲split的列表,並使用正則表達式匹配每個實體內的地址。偶爾,PDF已經被亂序轉換,並且實體會有兩個地址,這導致了不好)。

> 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 

當我告訴我的學生,成爲一名程序員正在改變你的心態部分來自「錯誤是煩人」到「錯誤是我的朋友。」

零長度輸入
一個相關的一點是,如果輸入長度是零,sapply將總是返回一個空列表,而不管輸入的類型。比較:

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) 

隨着vapply,保證您有一個特定類型的輸出,所以你不需要編寫額外的檢查零級長度的輸入。

基準

vapply可能有點快,因爲它已經知道什麼格式應該中期待的結果。

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

如果你總是希望你的結果是特別的東西...例如。一個邏輯向量。 vapply確保發生這種情況,但sapply不一定這樣做。

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

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

我認爲在這種情況下最顯而易見的事情是'logical(1)',因爲FALSE看起來像設置了一個選項「OFF」而不是指定一個類型 –

13

參與vapply額外的擊鍵可以節省您的時間後,調試混亂的結果。如果您要調用的函數可能返回不同的數據類型,則肯定應該使用vapply

想到的一個例子是RODBC包中的sqlQuery。如果執行查詢時發生錯誤,該函數返回帶有消息的character向量。因此,舉例來說,假設你正在試圖遍歷表名tnames的載體,在每個表請從數值列「NumCol」的最大值:

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

如果所有的表名是有效的,這將導致一個numeric向量。但是,如果其中一個表名稱在數據庫中發生更改並且查詢失敗,則結果將被強制轉換爲character模式。然而,使用vapplyFUN.VALUE=numeric(1),會在這裏停止錯誤,並防止它跳出某處 - 或者更糟的是,根本就不會彈出。

相關問題