LoginSignup
49

More than 5 years have passed since last update.

dplyr::filterの再確認

Last updated at Posted at 2017-03-07

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してからね

実例

サンプルのデータセットを準備します:

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!


  1. 欠損値への対応としては,tidyrパッケージにも便利な関数が用意してあります。こちらにまとめてますので参照してください。 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49