行(レコード)を条件によりフィルタリングして、必要な行(レコード)を残すという関数です。dplyrの中でもかなり高頻度で利用される基本的な関数のひとつです。

filter関数とは

使い方

filter(.data, ...)

引数

.data

対象とするデータセットを指定。

フィルタリングする条件を指定。

試しに、irisデータよりSepal.Length > 6を満たすレコードを抽出してみます:

library(dplyr)
filter(iris, Sepal.Length > 6)

このように、引数の...にピックアップしたい引数を指定すると持ってきてくれます。また、複数指定することもできます:

filter(iris, Sepal.Length > 6, Species == "versicolor")

出力から、このように続けて指定するとANDになるのがわかりますね。

ちょっと込み入った話

filter関数は...で受け取った内容を吟味し、.dataで受け取ったデータと同一の長さの論理値(TRUE, FALSE)ベクトルを準備して、それを使ってフィルタリングします。よって、論理値ベクトルをそのまま与えても反応します:

# 論理値ベクトルを準備
TF <- sample(c(TRUE, FALSE), size = 150, replace = TRUE, prob = c(1,9))
# TRUEの数を確認
summary(TF)
##    Mode   FALSE    TRUE 
## logical     120      30
# filterを実行
filter(iris, TF)

なお、この論理値ベクトルは行数と同一である必要があり,異なる場合エラーを返します。エラーが出ない特別な場合として,ベクトル長が1(要するにTRUEまたはFALSEがひとつだけ)がありますが,実質意味はないでしょう:

filter(iris, TRUE)

フィルターが全くかからない、あるいはレコードが空っぽになるといういときは、こういうケースに陥っていることがあるのでその時は確認してみてください。

エクササイズ1

irisデータセットには、以下の変数が含まれています:

names(iris)
## [1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width" 
## [5] "Species"

では、Petal.Length1.5未満のレコードを抽出してください。

これでOKです:

filter(iris, Petal.Length < 1.5)

実例

ここからは、以下のコードで作成したデータを用います:

df <- data.frame(x0 = 1:10,
                x1 = 11:20,
                s = sample(c("kosaki", "chitoge", "marika"), 10,
                           replace = TRUE),
                s2 = sample(c("kosaki", "chitoge", NA), 10,
                            replace = TRUE),
                row.names = letters[1:10])
df

数値フィルタ

まずは単一条件についてです:

# 「*に等しい」
filter(df, x0 == 4)
# 「*と等しくない」
filter(df, x0 != 4)
# 「*より小さい」
filter(df, x0 < 4)
# 「*以上」
filter(df, x0 >= 4)

このあたりは出力と比較すればすぐにわかると思います。

次に複数条件についてです:

# 「AかつB」(AND)
filter(df, x0 < 8 & x1 > 12)
# 「AまたはB」(OR)
filter(df, x0 > 8 | x1 < 15)
# 「AかつB」の余事象
filter(df, xor(x0 < 8, x1 > 12))
# 「AからB」
filter(df, dplyr::between(x0, 3, 7))

論理積(AND)は、上述のように,で繋いでいっても反応するのですが、明示的にしたほうがいいでしょう。betweenはdplyrに含まれる関数で、その名の通り区間を指定します。なお指定した範囲の値も含みます。

文字列フィルタ

まずは完全一致についてです:

# 「*と一致する」
filter(df, s == "kosaki")
# 「*と一致しない」
filter(df, s != "chitoge")

このあたりは数値フィルタと同一です。

次に、「複数の要素のどれかと一致する」ものを抽出する場合です:

filter(df, s %in% c("kosaki", "chitoge"))

この%in%はbaseにある演算子で、上記のように「Bの要素のどれか1つにマッチしたらTRUE、どれにもマッチしなければFALSE」を返してくれます。Rを使うなら絶対に覚えたほうがいいでしょう。

なお、この余事象である「複数の要素のどれとも一致しない」ものを抽出する場合です:

filter(df, !s %in% c("marika", "chitoge"))

非常に残念ながら%nin%みたいな演算子はbaseには入っていません。そのためそのような演算子を自分で定義するか、それを含むパッケージを読み込む、あるいは上記のコードのように頭に!をつけて逆にするあたりが妥当なところでしょう。

次に部分一致についてです。Rのbaseにはgrep系の関数もあるのですが、ここではtidyverseパッケージ群のひとつであるstringrパッケージから、str_detect関数を使います:

library(stringr)

# "k"を含む文字列で抽出
filter(df, str_detect(s, "k"))
# 先頭に"k"を含む文字列で抽出(正規表現によるパターン記述)
filter(df, str_detect(s, "^k"))

stringr::str_detect(string, pattern)はstringで指定した文字列ベクトルから正規表現でpatternと一致するものをTRUE,それ以外をFALSEで返してきます。greplを使うよりも簡単でかつ高速に処理する(はず)なのでこちらをおすすめします。

NA処理

is.na()関数を利用すると簡単に除去/抽出できます:

# s2にNAが含まれているレコードを抽出
filter(df, is.na(s2))
# s2にNAが含まれていないレコードを抽出
filter(df, !is.na(s2))

is.naは「NAか否か」を判定してTRUEあるいはFALSEを返してきます。なのでこの場合s2にNAがあるレコードを抽出してきます。そのため,!is.na(s2)と否定することでs2がNAではないレコードを抽出してくるようになります。

エクササイズ2

実例であげていたテクニックを使ってみましょう。

  1. dfデータセットから、x0が3以降で、s2がNAでないレコードを抽出しましょう
  2. dfデータセットから、sが“kosaki”あるいは“chitoge”で、x1が15より前のレコードを抽出しましょう

これでOKです:

# 1の実行例
filter(df, x0 >= 3 & !is.na(s2))
# 2の実行例
filter(df, s %in% c("kosaki", "chitoge") & x1 < 15)

Copyright © 2017 Kazutan.R.