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”答案。 :-) –