R論理演算子の数とmutate()での条件分岐
初めましてのブログです。最近はダーリン・イン・ザ・フランキス見てます。
今回は、
Rにおける理論演算子(&
,|
)の数によるエラーとtidyverse::mutate()
を用いた条件分岐
をいくらか紹介するコーナー
昨日まで、私はRにおける論理演算子である&
,|
は二つ並べて使うものだと思っていました。そこでこんなエラーと出会います。
まず、こんなデータセットがあります。
R version 3.5.3 (2019-03-11) -- "Great Truth" Copyright (C) 2019 The R Foundation for Statistical Computing Platform: x86_64-w64-mingw32/x64 (64-bit) >> library(tidyverse) > umr <- data.frame( + ID = as.factor(1:18), + method = as.factor(c("A","B","B", "A","A","A","A","A", + "B","B","B","B","A", "B","B","B","B","B")), + critelia = c(1,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1) + ) %>% + as.tibble %>% print() # A tibble: 18 x 3 ID method critelia <fct> <fct> <dbl> 1 1 A 1 2 2 B 0 3 3 B 0 4 4 A 1 5 5 A 1 6 6 A 1 7 7 A 1 8 8 A 1 9 9 B 1 10 10 B 0 11 11 B 0 12 12 B 0 13 13 A 1 14 14 B 1 15 15 B 1 16 16 B 1 17 17 B 1 18 18 B 1
ID
: 参加者ID
method
: 試した方法(例えば、2種類のダイエット方法)
critelia
: 成功したか(例えば、1は成功、0は失敗)
※架空のデータセット
このデータに対して、以下のような条件分岐を行いたい
method == A
かつcritelia == 1
なら新たな列に`1
または
method == B
かつcritelia == 0
なら新たな列に1
それ以外は0
を入れる
新たな列をclear_order
と名付け、で私はこうした。
> umr %>% + dplyr::mutate( + clear_order = ifelse((method == "A" && critelia == 1) || + (method == "B" && critelia == 0), 1, 0) + ) %>%print() # A tibble: 18 x 4 ID method critelia clear_order <fct> <fct> <dbl> <dbl> 1 1 A 1 1 2 2 B 0 1 3 3 B 0 1 4 4 A 1 1 5 5 A 1 1 6 6 A 1 1 7 7 A 1 1 8 8 A 1 1 9 9 B 1 1 10 10 B 0 1 11 11 B 0 1 12 12 B 0 1 13 13 A 1 1 14 14 B 1 1 15 15 B 1 1 16 16 B 1 1 17 17 B 1 1 18 18 B 1 1
全部1やん...
どうやらこれは、理論演算子の数が関係しているようだ。
詳しくは、以下の記事を参照↓↓
要するに
理論演算子1つで要素ごとに演算を行い、2つだとベクトルの1つ目の要素のみを演算する
つまり、今回のエラーは理論演算子を1つにすれば解決する。
Let's go!!
> umr %>% + dplyr::mutate( + clear_order = ifelse((method == "A" & critelia == 1) | + (method == "B" & critelia == 0), 1, 0) + ) %>% print() # A tibble: 18 x 4 ID method critelia clear_order <fct> <fct> <dbl> <dbl> 1 1 A 1 1 2 2 B 0 1 3 3 B 0 1 4 4 A 1 1 5 5 A 1 1 6 6 A 1 1 7 7 A 1 1 8 8 A 1 1 9 9 B 1 0 10 10 B 0 1 11 11 B 0 1 12 12 B 0 1 13 13 A 1 1 14 14 B 1 0 15 15 B 1 0 16 16 B 1 0 17 17 B 1 0 18 18 B 1 0
tidyverse::mutate()
を使って要素を一つずつ評価したいときは&
や|
の理論演算子は1つで行うべきなんですね。学びが深いなぁ...
mutate(new = if_else(条件A │ 条件B, 1, 0))
ちなみに、今回このエラーをとある秘密集団に投げかけたところ、別解を得られたので紹介。
別解1. case_when()
を使ったパターン
> umr %>% + dplyr::mutate( + clear_order = case_when( + critelia == 0 & method == "A" ~ 1, + critelia == 0 & method == "B" ~ 1, + TRUE ~ 0 + ) + ) %>% print() # A tibble: 18 x 4 ID method critelia clear_order <fct> <fct> <dbl> <dbl> 1 1 A 1 0 2 2 B 0 1 3 3 B 0 1 4 4 A 1 0 5 5 A 1 0 6 6 A 1 0 7 7 A 1 0 8 8 A 1 0 9 9 B 1 0 10 10 B 0 1 11 11 B 0 1 12 12 B 0 1 13 13 A 1 0 14 14 B 1 0 15 15 B 1 0 16 16 B 1 0 17 17 B 1 0 18 18 B 1 0
case_when()
も複数の条件を指定することが出来きる
~
の左辺が論理式(条件)
~
の右辺に置き換える値
って感じ。今回のケースは比較的論理式のコードが短いから良いが、コードが長くなるようであれば、こっちのほうが可読性が高い気がする。
case_when()
およびmutate()
については以下を参照↓↓
別解2. めっちゃ短いパターン
> umr %>% mutate(clear_order = ((method == "A") == critelia) %>% + as.integer()) %>% print() # A tibble: 18 x 4 ID method critelia clear_order <fct> <fct> <dbl> <int> 1 1 A 1 1 2 2 B 0 1 3 3 B 0 1 4 4 A 1 1 5 5 A 1 1 6 6 A 1 1 7 7 A 1 1 8 8 A 1 1 9 9 B 1 0 10 10 B 0 1 11 11 B 0 1 12 12 B 0 1 13 13 A 1 1 14 14 B 1 0 15 15 B 1 0 16 16 B 1 0 17 17 B 1 0 18 18 B 1 0
おいおい、瞬殺だよ...
これは短い。知らなかったけど、RはTRUE
を1、FALSE
を0の整数として扱うんですね。
(method == "A")
で"A"
ならTRUE(1)
,"B"
ならFALSE(0)
に変換critelia
は元から0,1データなので合致か否かでclear_order
行にTRUE
,FALSE
を返す。as.integer()
でTRUE
,FALSE
を1,0に変換
as.integer()
を使わないとTRUE
,FALSE
で返ってくるよ
ナイス、スマート
別解3. 自作関数を使ったパターン
> judge_order <- function(method, critelia) { + #空のベクトルを生成 + res <- vector() + #要素ごとに評価するためにfor文 + for (i in 1:length(method)) { + if (method[i] == "A" & critelia[i] == 1) { + res[i] <- 1 + } else if (method[i] == "B" & critelia[i] == 0) { + res[i] <- 1 + } else { + res[i] <- 0 + } + } + return(res) + } > umr %>% mutate(clear_order = judge_order(method, critelia)) %>% print() # A tibble: 18 x 4 ID method critelia clear_order <fct> <fct> <dbl> <dbl> 1 1 A 1 1 2 2 B 0 1 3 3 B 0 1 4 4 A 1 1 5 5 A 1 1 6 6 A 1 1 7 7 A 1 1 8 8 A 1 1 9 9 B 1 0 10 10 B 0 1 11 11 B 0 1 12 12 B 0 1 13 13 A 1 1 14 14 B 1 0 15 15 B 1 0 16 16 B 1 0 17 17 B 1 0 18 18 B 1 0
実は、最初は自作関数を使ってトライして失敗しました...
詳しくは分からないがtidyval案件で、難しいみたい