文件说
vapply
类似于sapply
,但具有预先指定的返回值类型,因此使用起来更安全[...]。
您能否详细说明为什么它通常更安全,也许提供示例?
PS:我知道答案,我已经倾向于避免sapply
。我只是希望这里有一个很好的答案,所以我可以向我的同事指出它。请不要“阅读手册”答案。
如前所述,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
因为 input2 的第三个元素中有两个 d,所以 vapply 会产生错误。但是 sapply 将输出的类从字符向量更改为列表,这可能会破坏下游代码。
正如我告诉我的学生,成为程序员的一部分是将你的思维模式从“错误很烦人”转变为“错误是我的朋友”。
零长度输入
与此相关的一点是,如果输入长度为零,sapply
则无论输入类型如何,都将始终返回一个空列表。比较:
sapply(1:5, identity)
## [1] 1 2 3 4 5
sapply(integer(), identity)
## list()
vapply(1:5, identity, integer(1))
## [1] 1 2 3 4 5
vapply(integer(), identity, integer(1))
## 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
可以节省您稍后调试令人困惑的结果的时间。如果您调用的函数可以返回不同的数据类型,vapply
那么当然应该使用。
想到的一个例子是sqlQuery
在RODBC
包装中。如果执行查询时出错,此函数将返回character
带有消息的向量。因此,例如,假设您尝试遍历表名向量tnames
并从每个表中的数字列“NumCol”中选择最大值:
sapply(tnames,
function(tname) sqlQuery(cnxn, paste("SELECT MAX(NumCol) FROM", tname))[[1]])
如果所有表名都有效,这将产生一个numeric
向量。但是如果其中一个表名在数据库中发生更改并且查询失败,则结果将被强制转换为 mode character
。但是,使用vapply
withFUN.VALUE=numeric(1)
将在此处停止错误并防止它在某个地方弹出——或者更糟,根本不会。
如果你总是希望你的结果是特别的……例如一个逻辑向量。 vapply
确保发生这种情况,但sapply
不一定这样做。
a<-vapply(NULL, is.factor, FUN.VALUE=logical(1))
b<-sapply(NULL, is.factor)
is.logical(a)
is.logical(b)