dplyr::filterの基礎
dplyr::filter
はレコードをフィルタリングして必要なレコードを残すという関数です。dplyr
パッケージに組み込まれていますので,使うためにはdplyr
を読み込む必要があります:
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
使い方
filter(.data, ...)
.data
: 対象となるデータセット。
...
: .dataに含まれる変数で定義された論理式。
留意事項
関数のヘルプドキュメントに以下のような留意事項がありました:
- データセットのrownamesは黙って削除される
- もし残したかったら
tibble::rownames_to_column
を実行しといてね
- もし残したかったら
- group_byしたデータセットの場合,うまく最適化されてない
- filter処理する際はグループ化する前,もしくは
ungroup
してからね
- filter処理する際はグループ化する前,もしくは
実例
サンプルのデータセットを準備します:
d <- data.frame(x0 = 1:10,
x1 = 11:20,
s = sample(c("kosaki", "chitoge", "marika"), 10, replace = TRUE),
row.names = letters[1:10]
)
d
#> x0 x1 s
#> a 1 11 marika
#> b 2 12 kosaki
#> c 3 13 chitoge
#> d 4 14 chitoge
#> e 5 15 marika
#> f 6 16 kosaki
#> g 7 17 chitoge
#> h 8 18 marika
#> i 9 19 kosaki
#> j 10 20 chitoge
基本的な考え方
filterは,要するに**...
引数にTRUE
もしくはFALSE
の論理値ベクトルを与えればOK**です。実際に直接指定することはないでしょうが,以下のコードでちゃんと動きます:
f <- c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE)
filter(d, f)
#> x0 x1 s
#> 1 1 11 marika
#> 2 2 12 kosaki
#> 3 3 13 chitoge
#> 4 7 17 chitoge
#> 5 8 18 marika
ただし,この論理値ベクトルは行数と同一である必要があり,異なる場合エラーを返します。エラーが出ない特別な場合として,ベクトル長が1(要するにTRUE
またはFALSE
がひとつだけ)がありますが,実質意味はないでしょう:
filter(d, TRUE)
#> x0 x1 s
#> a 1 11 marika
#> b 2 12 kosaki
#> c 3 13 chitoge
#> d 4 14 chitoge
#> e 5 15 marika
#> f 6 16 kosaki
#> g 7 17 chitoge
#> h 8 18 marika
#> i 9 19 kosaki
#> j 10 20 chitoge
フィルターが全くかからない,あるいはレコードが空っぽになる,というときはこういうケースに陥っていることもあるかと思います。
数値フィルタ
まずは単一条件についてです。
filter(d, x0==4)
#> x0 x1 s
#> 1 4 14 chitoge
filter(d, x0!=4)
#> x0 x1 s
#> 1 1 11 marika
#> 2 2 12 kosaki
#> 3 3 13 chitoge
#> 4 5 15 marika
#> 5 6 16 kosaki
#> 6 7 17 chitoge
#> 7 8 18 marika
#> 8 9 19 kosaki
#> 9 10 20 chitoge
filter(d, x0<4)
#> x0 x1 s
#> 1 1 11 marika
#> 2 2 12 kosaki
#> 3 3 13 chitoge
filter(d, x0>=4)
#> x0 x1 s
#> 1 4 14 chitoge
#> 2 5 15 marika
#> 3 6 16 kosaki
#> 4 7 17 chitoge
#> 5 8 18 marika
#> 6 9 19 kosaki
#> 7 10 20 chitoge
このあたりは出力と比較すればすぐにわかるかと思います。
次に複数条件についてです。
filter(d, x0<8 & x1>12)
#> x0 x1 s
#> 1 3 13 chitoge
#> 2 4 14 chitoge
#> 3 5 15 marika
#> 4 6 16 kosaki
#> 5 7 17 chitoge
filter(d, x0>8 | x1<15)
#> x0 x1 s
#> 1 1 11 marika
#> 2 2 12 kosaki
#> 3 3 13 chitoge
#> 4 4 14 chitoge
#> 5 9 19 kosaki
#> 6 10 20 chitoge
filter(d, xor(x0<8, x1>12))
#> x0 x1 s
#> 1 1 11 marika
#> 2 2 12 kosaki
#> 3 8 18 marika
#> 4 9 19 kosaki
#> 5 10 20 chitoge
filter(d, dplyr::between(x0, 3, 7))
#> x0 x1 s
#> 1 3 13 chitoge
#> 2 4 14 chitoge
#> 3 5 15 marika
#> 4 6 16 kosaki
#> 5 7 17 chitoge
xor(x, y)
は「xかつy」の余事象となります。dplyr::between
は,その名の通り区間を指定します。指定した範囲の値を含むものとなります。
文字列フィルタ
まず完全一致についてです。
filter(d, s=="kosaki")
#> x0 x1 s
#> 1 2 12 kosaki
#> 2 6 16 kosaki
#> 3 9 19 kosaki
filter(d, s!="chitoge")
#> x0 x1 s
#> 1 1 11 marika
#> 2 2 12 kosaki
#> 3 5 15 marika
#> 4 6 16 kosaki
#> 5 8 18 marika
#> 6 9 19 kosaki
このあたりは数値フィルタと同様です。
次に正規表現を利用した一致条件の指定です。この場合,stringr::str_ detect
関数が便利です。
library(stringr)
filter(d, str_detect(s, "k"))
#> x0 x1 s
#> 1 1 11 marika
#> 2 2 12 kosaki
#> 3 5 15 marika
#> 4 6 16 kosaki
#> 5 8 18 marika
#> 6 9 19 kosaki
filter(d, str_detect(s, "^k"))
#> x0 x1 s
#> 1 2 12 kosaki
#> 2 6 16 kosaki
#> 3 9 19 kosaki
stringr::str_detect(string, pattern)
はstring
で指定した文字列ベクトルから正規表現でpattern
と一致するものをTRUE
,それ以外をFALSE
で返してきます。grepl
を使うよりも簡単でかつ高速に処理する(はず)なのでこちらをおすすめします。
複数の文字列に一致するor一致しないを指定する方法です。
filter(d, s %in% c("kosaki", "marika"))
#> x0 x1 s
#> 1 1 11 marika
#> 2 2 12 kosaki
#> 3 5 15 marika
#> 4 6 16 kosaki
#> 5 8 18 marika
#> 6 9 19 kosaki
filter(d, !(s %in% c("kosaki", "marika")))
#> x0 x1 s
#> 1 3 13 chitoge
#> 2 4 14 chitoge
#> 3 7 17 chitoge
#> 4 10 20 chitoge
%in%
演算子は,左側の各要素が右側の要素のどれかにマッチするかを判定してTRUE
もしくはFALSE
の論理値ベクトルを返します。逆に右側の要素のどれにもマッチしないものを取り出したい場合,上記のようにします。ただ正直これはスマートなコードではないので,同じ処理をする %nin%
を定義したり,あるいは実装したパッケージもあります。個人的にはbaseに組み込んでほしいところなのですが…。一応定義するとしたら,こういう感じでOKです:
'%nin%' <- function(x, y) !(x %in% y)
filter(d, s %nin% c("kosaki", "chitoge"))
#> x0 x1 s
#> 1 1 11 marika
#> 2 5 15 marika
#> 3 8 18 marika
NA処理
ちょcっとデータセットを変更します:
d_withNA <- d <- data.frame(x0 = 1:10,
x1 = 11:20,
s = sample(c("kosaki", "chitoge", NA), 10, replace = TRUE),
row.names = letters[1:10]
)
d_withNA
#> x0 x1 s
#> a 1 11 <NA>
#> b 2 12 kosaki
#> c 3 13 kosaki
#> d 4 14 <NA>
#> e 5 15 kosaki
#> f 6 16 chitoge
#> g 7 17 kosaki
#> h 8 18 chitoge
#> i 9 19 <NA>
#> j 10 20 <NA>
この場合,このような処理が可能です:
filter(d_withNA, is.na(s))
#> x0 x1 s
#> 1 1 11 <NA>
#> 2 4 14 <NA>
#> 3 9 19 <NA>
#> 4 10 20 <NA>
filter(d_withNA, !is.na(s))
#> x0 x1 s
#> 1 2 12 kosaki
#> 2 3 13 kosaki
#> 3 5 15 kosaki
#> 4 6 16 chitoge
#> 5 7 17 kosaki
#> 6 8 18 chitoge
is.na
は「NAか否か」を判定してTRUEあるいはFALSEを返してきます。なのでこの場合sにNA
があるレコードを抽出してきます。そのため,!is.na(s)
と否定することでsがNAではないレコードを抽出してくるようになります1。
このあたりが使えれば,あとは条件を組み合わせることで望むようなレコード抽出ができるようになるでしょう。
Enjoy!