2012-09-09 40 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),会在这里停止错误,并防止它跳出某处 - 或者更糟的是,根本就不会弹出。

相关问题