1. ホーム
  2. r

[解決済み] なぜ `vapply` は `sapply` よりも安全なのですか?

2023-06-03 19:18:56

質問

ドキュメントによると

vapplysapply と似ていますが、戻り値の型があらかじめ指定されているので、より安全に[...]使用することができます。

なぜそれが一般的により安全なのか、例を挙げて詳しく説明していただけませんか?


追伸:答えはわかっていますし、私はすでに sapply . ただ、このSOに素敵な答えがあれば、同僚にそれを示すことができるのに、と思っています。どうか、「マニュアルを読め」的な回答は無しでお願いします。

どのように解決するのですか?

すでに指摘されているように vapply は2つのことをします。

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

2点目は、エラーを事前にキャッチしやすくなり、より堅牢なコードにつながるという大きなメリットです。 この戻り値のチェックは、別途 sapply の後に stopifnot を使用して、戻り値が期待したものと一致することを確認しますが vapply の方が少し簡単です(カスタムのエラーチェックのコードで境界内の値をチェックできるなど、より限定的ではありますが)。

以下は vapply を実行し、期待通りの結果が得られることを確認します。 これは、ちょうど私がPDFスクレイピングをしているときに取り組んでいたことと類似しています。 findD 正規表現 を使って生のテキストデータのパターンにマッチさせます (例えば、リストが split というリストがあり、各エンティティ内の住所に一致する正規表現がありました。 時々、PDF が順番通りに変換されず、1 つのエンティティに 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

input2の3番目の要素にdが2つあるので、vapplyはエラーを発生させます。 しかし、sapplyは出力のクラスを文字ベクトルからリストに変更するので、下流のコードを破壊する可能性があります。

私は学生たちに、プログラマーになることの一部は、「エラーは迷惑だ」という考え方から「エラーは友達だ」という考え方に変えることだと言っています。

ゼロ長入力

関連する点として、入力の長さがゼロの場合は sapply は入力の型に関係なく常に空のリストを返すということです。 比較してみてください。

sapply(1:5, identity)
## [1] 1 2 3 4 5
sapply(integer(), identity)
## list()    
vapply(1:5, identity, integer(1))
## [1] 1 2 3 4 5
vapply(integer(), identity, integer(1))
## integer(0)

とは vapply を使えば、特定の種類の出力が保証されるので、長さが0の入力に対する余分なチェックを書く必要はない。

ベンチマーク

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)

<イグ