vapply
類似於sapply
,但有一個預先指定的返回值的類型,所以可以更安全[...]使用。
請您詳細說明爲什麼它通常更安全,也許提供示例?
P.S .:我知道答案,我已經傾向於避免sapply
。我只希望在這裏有一個很好的答案,所以我可以指出我的同事。請不要「閱讀手冊」的答案。
vapply
類似於sapply
,但有一個預先指定的返回值的類型,所以可以更安全[...]使用。
請您詳細說明爲什麼它通常更安全,也許提供示例?
P.S .:我知道答案,我已經傾向於避免sapply
。我只希望在這裏有一個很好的答案,所以我可以指出我的同事。請不要「閱讀手冊」的答案。
正如已經指出的那樣,vapply
做了兩兩件事:
第二點是更大的優勢,因爲它有助於在錯誤發生之前捕捉錯誤並導致更健壯的代碼。這個返回值檢查可以通過使用sapply
然後stopifnot
以確保返回值與您的預期一致來單獨完成,但vapply
更容易一些(如果更有限,由於自定義錯誤檢查代碼可以檢查邊界內的值等)。
下面是vapply
的一個示例,確保您的結果符合預期。這與我剛剛在PDF抓取時正在進行的工作相似,其中findD
將使用regex來匹配原始文本數據中的模式(例如,我將按實體列出一個爲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)
如果你總是希望你的結果是特別的東西...例如。一個邏輯向量。 vapply
確保發生這種情況,但sapply
不一定這樣做。
a<-vapply(NULL, is.factor, FUN.VALUE=logical(1))
b<-sapply(NULL, is.factor)
is.logical(a)
is.logical(b)
我認爲在這種情況下最顯而易見的事情是'logical(1)',因爲FALSE看起來像設置了一個選項「OFF」而不是指定一個類型 –
參與vapply
額外的擊鍵可以節省您的時間後,調試混亂的結果。如果您要調用的函數可能返回不同的數據類型,則肯定應該使用vapply
。
想到的一個例子是RODBC
包中的sqlQuery
。如果執行查詢時發生錯誤,該函數返回帶有消息的character
向量。因此,舉例來說,假設你正在試圖遍歷表名tnames
的載體,在每個表請從數值列「NumCol」的最大值:
sapply(tnames,
function(tname) sqlQuery(cnxn, paste("SELECT MAX(NumCol) FROM", tname))[[1]])
如果所有的表名是有效的,這將導致一個numeric
向量。但是,如果其中一個表名稱在數據庫中發生更改並且查詢失敗,則結果將被強制轉換爲character
模式。然而,使用vapply
和FUN.VALUE=numeric(1)
,會在這裏停止錯誤,並防止它跳出某處 - 或者更糟的是,根本就不會彈出。
它更具可預測性,使得代碼更模糊,更健壯。特別是在大型項目中,比如說一個大型包裝,這是相關的。 –
不需要「P.S.」......只是自問這個問題。 –
@KonradRudolph有時候這種效果的註釋可以集中問題並避免「RTFM」答案。 :-) –