为什么“ vapply”比“ sapply”更安全?


84

该文件说

vapply类似于sapply,但是具有预先指定的返回值类型,因此可以更安全地使用。

您能否解释一下为什么它通常更安全,也许提供示例?


PS:我知道答案,我已经倾向于避免了sapply。我只是希望在SO上有一个不错的答案,这样我可以指出我的同事。请没有“阅读手册”的答案。


1
它更具可预测性,从而使代码减少了歧义,并且更加健壮。特别是在大型项目(例如大型项目)中,这很重要。
Paul Hiemstra 2012年

1
vapply的FUN.VALUE手动示例非常复杂,对于精明的用户而言是令人生畏的。
jsta

Answers:


73

如前所述,vapply要做两件事:

  • 速度略有提高
  • 通过提供有限的返回类型检查来提高一致性。

第二点是更大的优势,因为它有助于在错误发生之前捕获错误,并导致更健壮的代码。此返回值检查可以通过单独使用来完成sapplystopifnot以确保返回值与您期望的值一致,但是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)

自动绘图


15

涉及的额外按键vapply可以节省您以后调试时产生混淆的时间。如果您要调用的函数可以返回不同的数据类型,则vapply应该使用。

我想到的一个例子就是sqlQueryRODBC包中。如果执行查询时出错,则此函数返回character带有消息的向量。例如,假设您要遍历表名称的向量,tnames并使用以下命令从每个表的数字列“ NumCol”中选择最大值:

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

如果所有表名均有效,则将产生一个numeric向量。但是,如果表名之一在数据库中发生更改,并且查询失败,则结果将被强制转换为mode character。但是,vapply与with一起使用FUN.VALUE=numeric(1)将在此处停止该错误并阻止其突然出现在某处-或更糟糕的是,根本没有出现。


13

如果您始终希望结果特别重要,例如逻辑向量。 vapply确保发生这种情况,但sapply不一定一定会发生这种情况。

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

is.logical(a)
is.logical(b)

4
我认为最明显的事情就是logical(1)这种情况,因为FALSE似乎将选项设置为“ OFF”而不是指定类型
飞羊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.