dplyr 1.0.0から導入されたacross
関数は、mutate
関数やsummarize
関数を複数列に簡単に適用できる便利な道具です。
*_at
や*_if
といった関数を過去のものにした他、group_by
でも使えるなど、使いどころは多いです。
- dplyr Version 1.0.0 新機能 2 列操作 : > across by @shunido113さん
- dplyr 1.0.0を使ってみる: across(), rename_with() by @yutannihilationさん
ただし、@yutannihilationさんも指摘している通り、filter
関数内でのacross
の利用はお世辞にも直感的ではありませんでした。たとえば、numericな列のいずれかが0.1未満な行を抽出するならこんな感じ。
library(magrittr)
set.seed(9)
d <- data.frame(x = runif(20), y = runif(20), z = letters[1:20])
d %>%
dplyr::filter(
rowSums(dplyr::across(where(is.numeric), function(x) x < 0.1)) > 0
)
#> x y z
#> 1 0.024233910 0.020831235 b
#> 2 0.008471164 0.972864807 l
#> 3 0.492743506 0.004426156 o
across
の返り値はデータフレームであることを利用し、各行の総和をとれば、1つでもTRUE
な行は1以上の値をとるわけですね。ということは、すべてが0.1未満な行を抽出しようとすると、行の積をとらなければなりません。
rowProdがないので面倒ですね。あと、括弧が多過ぎてわけわからない……。
dplyr 1.0.4では新たにif_any
関数とif_all
関数を導入し、この問題に対処します。たとえば先の例をif_any
で書き直すとこんな感じ。
d %>%
dplyr::filter(
dplyr::if_any(where(is.numeric), function(x) x < 0.1)
)
#> x y z
#> 1 0.024233910 0.020831235 b
#> 2 0.008471164 0.972864807 l
#> 3 0.492743506 0.004426156 o
across
関数に代わりにif_any
関数を使い、rowSums
関数や>
演算子とはおさらばです。すべての列が条件を満たす場合も、if_any
関数をif_all
関数に書き直すだけ。
d %>%
dplyr::filter(
dplyr::if_all(where(is.numeric), function(x) x < 0.1)
)
#> x y z
#> 1 0.02423391 0.02083123 b
ちなみに本機能を紹介するTidyverseのBlog記事「dplyr 1.0.4: if_any() and if_all()」で紹介されている通り、if_any
やif_all
はmutate
やsummarize
の中でも使えます。
d %>%
head() %>%
dplyr::mutate(
all_low = dplyr::if_all(where(is.numeric), function(x) x < 0.1)
)
#> x y z all_low
#> 1 0.22160140 0.89929956 a FALSE
#> 2 0.02423391 0.02083123 b TRUE
#> 3 0.20711902 0.31957158 c FALSE
#> 4 0.21573355 0.11292940 d FALSE
#> 5 0.44372359 0.52832265 e FALSE
#> 6 0.13407615 0.91291264 f FALSE
他にもdplyr 1.0.4ではacross
関数使用時のパフォーマンスが改善されたらしいですね。素晴らしい!
Enjoy!