ベクトルから要素を除去する方法とsetdiffの罠

by
カテゴリ:

以下のxからyを除去してみましょう。

x <- c('banana', 'banana', 'apple', 'grape')
y <- c('apple', 'grape')

%in%演算子を使えばxの要素がyに含まれているか判定できるので、簡単ですね。

x[!x %in% y]
#> [1] "banana" "banana"

もっと簡単「そう」な方法に、setdiff関数があります。ただしこいつは中でunique関数をかけている点に注意が必要です。

setdiff(x, y)
#> [1] "banana"

そうとは知らずrmarkdownへのPRで、提案通りにsetdiff関数を使ったところ、大失敗を生んでしまいました( https://github.com/rstudio/rmarkdown/pull/1932)。レビュアー2人いて気付かずにマージされてしまったくらいなので、皆さんも注意しましょう……。

ちなみに、setdiff関数は内部で色々面倒なことをしているのもあって速度も遅めです。場合によってはx[!x %in% y]やそれにunique関数を加える方が良いかもしれませんね。 setdiffはbaseに含まれていて、バイトコンパイルされているので、他の方法も明示的にバイトコンパイルしてからベンチマークしています。

x <- sample(letters, 1000, TRUE)
y <- sample(letters, 10, FALSE)
drop <- compiler::cmpfun(function(x, y) x[!x %in% y])
drop_and_unique <- compiler::cmpfun(function(x, y) {
  x <- unique(x)
  x[!x %in% y]
})
ggplot2::autoplot(bench::mark(
  drop(x, y),
  drop_and_unique(x, y),
  setdiff(x, y),
  check = FALSE
))
#> Loading required namespace: tidyr

Enjoy!