web-dev-qa-db-ja.com

「vapply」が「sapply」より安全なのはなぜですか?

ドキュメントには

vapplysapplyと似ていますが、戻り値のタイプが事前に指定されているため、[...]を使用する方が安全です。

一般的に安全である理由を詳しく説明してください。例を挙げてください。


追伸:私は答えを知っており、すでにsapplyを避ける傾向があります。ここでSOにすてきな答えがあったらいいので、同僚にそれを指摘できます。「マニュアルを読む」という答えはありません。

81
flodel

既に述べたように、vapplyは2つのことを行います。

  • わずかな速度改善
  • 限定された戻り型チェックを提供することにより、一貫性が向上します。

2番目の点は、エラーが発生する前にキャッチするのに役立ち、より堅牢なコードにつながるため、大きな利点です。この戻り値のチェックは、sapplyに続いてstopifnotを使用して、戻り値が予期したものと一致することを確認することで個別に実行できますが、vapplyの方が簡単です(より制限されている場合は、カスタムエラーチェックコードが範囲内の値をチェックできるなど)。

以下に、結果が期待どおりであることを確認するvapplyの例を示します。これは、PDFスクレイピングで、findDregex を使用して生のテキストデータのパターンに一致する間、私がちょうど取り組んでいたものに似ています。エンティティごとにsplitであるリストと、各エンティティ内のアドレスに一致する正規表現があります。ときどきPDFは順不同に変換され、エンティティの2つのアドレス、これにより不良が発生しました)。

> 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

私が生徒たちに言ったように、プログラマーになることの一部は、あなたの考え方を「エラーはわずらわしい」から「エラーは私の友達」に変えることです。

長さゼロの入力
関連するポイントの1つは、入力長がゼロの場合、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

68
Ari B. Friedman

vapplyに関連する余分なキーストロークにより、後で混乱する結果をデバッグする時間を節約できます。呼び出している関数が異なるデータ型を返すことができる場合は、vapplyを必ず使用する必要があります。

頭に浮かぶ1つの例は、sqlQueryパッケージのRODBCです。クエリの実行中にエラーが発生した場合、この関数はメッセージとともにcharacterベクトルを返します。たとえば、テーブル名のベクトルtnamesを反復処理し、各テーブルの数値列 'NumCol'から最大値を選択しようとしているとします。

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

すべてのテーブル名が有効な場合、これはnumericベクトルになります。ただし、データベースでテーブル名の1つが変更され、クエリが失敗した場合、結果は強制的にモードcharacterになります。ただし、vapplyFUN.VALUE=numeric(1)とともに使用すると、ここでエラーが停止し、行のどこかにポップアップが表示されなくなります。

15
Matthew Plourde

結果を常に特定のものにしたい場合...論理ベクトル。 vapplyはこれが確実に行われるようにしますが、sapplyは必ずしもそうではありません。

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

is.logical(a)
is.logical(b)
13
user1317221_G