为什么"vapply"比"sapply"更安全?

flo*_*del 81 r apply r-faq

文件说

vapply类似于sapply,但具有预先指定的返回值类型,因此使用起来更安全.

你能详细说明为什么它通常更安全,可能提供例子吗?


PS:我知道答案,我已经倾向于避免sapply.我只希望在这里有一个很好的答案,所以我可以指出我的同事.请不要"阅读手册"的答案.

Ari*_*man 68

正如已经指出的那样,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
Run Code Online (Sandbox Code Playgroud)

正如我告诉我的学生,成为程序员的一部分正在改变你的心态,从"错误令人讨厌"到"错误是我的朋友".

零长度输入
一个相关点是,如果输入长度为零,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)
Run Code Online (Sandbox Code Playgroud)

使用时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)
Run Code Online (Sandbox Code Playgroud)

自动绘制


Mat*_*rde 15

所涉及的额外击键vapply可以节省您以后调试混乱结果的时间.如果您正在调用的函数可以返回不同的数据类型,vapply那么当然应该使用.

我想到的一个例子就是sqlQueryRODBC包中.如果执行查询时出错,则此函数返回character带有消息的向量.因此,例如,假设您正在尝试tnames迭代表名向量,并从每个表中的数字列"NumCol"中选择最大值:

sapply(tnames, 
   function(tname) sqlQuery(cnxn, paste("SELECT MAX(NumCol) FROM", tname))[[1]])
Run Code Online (Sandbox Code Playgroud)

如果所有表名都有效,则会产生numeric向量.但是,如果其中一个表名在数据库中发生更改并且查询失败,则结果将被强制转换为模式character.然而,使用vapplywith FUN.VALUE=numeric(1)会在这里停止错误并防止它在某个地方弹出 - 或者更糟糕的是,根本不会.


use*_*1_G 13

如果你总是希望你的结果特别适合......例如逻辑矢量. vapply确保这种情况发生,但sapply不一定如此.

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

is.logical(a)
is.logical(b)
Run Code Online (Sandbox Code Playgroud)

  • 我认为最明显的事情是`logical(1)`在这种情况下,因为FALSE看起来像是设置一个选项为"OFF"而不是指定一个类型 (4认同)