dplyrのベクトル要素変換用関数3種

dplyrパッケージの現行バージョン (0.5.0)ではデータ操作の中で値を変更するための関数がいくつか用意されている。これらの関数の使い道と使い分けについてメモしておく。

Shinya Uryu

4 minute read

library(dplyr)

{dplyr}パッケージの現行バージョン (0.5.0) ではデータ操作の中で値を変更するための関数がいくつか用意されている。これらの関数の使い道と使い分けについてメモしておく。

簡単に整理するとこんな感じになる

関数 特徴
if_else() ifelse()の拡張。欠損値に対する挙動も指定できる
case_when() ベクトルの各要素に対する挙動を指定。指定しない場合には欠損値として扱われるので注意
recode() ベクトル内の特定の値だけを変更したいときに効果的。変換後の型に注意

if_else関数

ドキュメントによるとこの関数は、R標準の制御関数の一つであるifelse()と比べてより厳密 strict なものであるらしい。ここでの strict とはどういうことか。yutannihilation さんがこんな記事を書いてくれたのでこちらを読んでほしい。

ifelse()は危険なのでやめてdplyr::if_else()を使いましょう

真偽値の判定を実行するコード(xはyよりも大きい、xのクラスはzである、とか)と、真偽それぞれの状況の応答を引数に与えて実行する。またこの関数は、それだけでなくxの値が欠損値である場合についても特定の処理を実行するという特徴をもつ。欠損値に対する挙動は引数missingによって与える。

x <- c(-3:3, NA)
if_else(x < 0, "negative", "positive", missing = "missing")
## [1] "negative" "negative" "negative" "positive" "positive" "positive"
## [7] "positive" "missing"

この関数はもちろん {dplyr}mutate()内で利用できる。

iris %>% head() %>% 
  mutate(Sepal.Size = if_else(Sepal.Length > 6.0, TRUE, FALSE))
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal.Size
## 1          5.1         3.5          1.4         0.2  setosa      FALSE
## 2          4.9         3.0          1.4         0.2  setosa      FALSE
## 3          4.7         3.2          1.3         0.2  setosa      FALSE
## 4          4.6         3.1          1.5         0.2  setosa      FALSE
## 5          5.0         3.6          1.4         0.2  setosa      FALSE
## 6          5.4         3.9          1.7         0.4  setosa      FALSE

if_else()では真偽値の判定を入れ子にして複数の分岐を作成することもできるが、データの値を修正する場合には、次のcase_when()を用いるのが良い。

case_when関数

変換候補が複数あるとき、if_else()よりもcase_when()を使うのが効率的だ。case_when()では関数名の通り、条件分岐によるベクトル内の要素の変更を行う。書き方が特殊なので慣れが必要。引数内で条件式とそれに該当する値の変更値をチルダ記号(~)で繋いで指定する。

x <- c(-3:3, NA)
case_when(
  x < 0 ~ "negative",
  x > 0 ~ "positive")
## [1] "negative" "negative" "negative" NA         "positive" "positive"
## [7] "positive" NA

上記のように引数に与えた複数の条件に対して値が変更されるが、気をつけないといけないのが要素の各値に対する挙動である。個々の挙動を指定しないと、条件に当てはまらない、つまり真となる条件のない要素については欠損値に置換されてしまう。先の例では元の欠損値に加え、0もNAになっている。

で、これをmutate()の中で使うときの注意点。mutate()の中で関数を利用する場合、変数の参照は「.$」で行うということを思い出して次のようにする。

df.animals <- tibble::frame_data(
  ~team, ~rank,
  "mouse", 1,
  "cow", 2,
  "tiger", 3,
  "rabbit", 4
)

df.animals %>% 
  mutate(
    rank = case_when(
      .$team == "mouse" ~ 3,
      .$team == "cow" ~ 2,
      .$team == "tiger" ~ 1,
      .$team == "rabbit" ~3
    )
  )
## # A tibble: 4 × 2
##     team  rank
##    <chr> <dbl>
## 1  mouse     3
## 2    cow     2
## 3  tiger     1
## 4 rabbit     3

recode

もう一つの関数、recode()。これはより細かいというか、個々の値に対して変換をしたいときに便利。これも引数で「対象の値 = 変換後の値」を記述する形式となる。変換するものがなければ省略して良いし、変換後の値だけを引数に与えても良い。その場合には要素の順に変換の対象としていく(この挙動がいまいち掴めない…)。

x <- c(-3:3, NA)
recode(x, `3` = 6L, `-3` = -8L)
## [1] -8 -2 -1  0  1  2  6 NA

# 1, 2, 3... が対象となる
recode(x, 6L, -8L, 10L)
## [1] -3 -2 -1  0  6 -8 10 NA

ベクトル内の要素は同じ型でなければいけないので、次のように整数と実数や数値ベクトルに文字列を混ぜようとすると怒られる。怒られるだけでなく、その他の値は欠損値となってしまう。

# integerとnumericも区別されるので注意
recode(x, `3` = 6)
## [1] NA NA NA NA NA NA  6 NA
recode(x, `3` = "three")
## [1] NA      NA      NA      NA      NA      NA      "three" NA

すべての要素の挙動を指定する必要はないが、指定したもの以外の値について.default引数を用いて一括で指定できる。

recode(x, `3` = "three", .default = "numeric")
## [1] "numeric" "numeric" "numeric" "numeric" "numeric" "numeric" "three"  
## [8] NA

同様に欠損値に対しても.missing引数での置換を行える。

recode(x, `3` = "three", .default = "numeric", .missing = "missing")
## [1] "numeric" "numeric" "numeric" "numeric" "numeric" "numeric" "three"  
## [8] "missing"

私はmutate()と組み合わせてこういう使い方をしばしば行う。ここでは「.$」を使わない

df.animals %>% 
  mutate(所属 = recode(team,
                     mouse  = "子",
                     cow    = "丑",
                     tiger  = "寅",
                     rabbit = "卯"))
## # A tibble: 4 × 3
##     team  rank  所属
##    <chr> <dbl> <chr>
## 1  mouse     1    子
## 2    cow     2    丑
## 3  tiger     3    寅
## 4 rabbit     4    卯

ベクトルをカテゴリデータにするrecode_factor()関数もあるがここでは割愛。

comments powered by Disqus