126  実践 if_else

library(tidyverse)

まずはif_elseの簡単なケースを見ていきましょう。if_else関数はスライドでの説明があった通り、与えたロジカルベクトルに対してTRUEの場合、FALSEの場合にそれぞれ違う要素を返してくれる関数です。

if_else(TRUE, "trueです", "falseです")
[1] "trueです"
if_else(FALSE, "trueです", "falseです")
[1] "falseです"
if_else(c(T,F,T), "trueです","falseです")
[1] "trueです"  "falseです" "trueです" 

このように、TとFの場合でそれぞれ違う結果が与えた長さだけ返ってきていることがわかりますか?

これとmutateを組み合わせると、

dat <- tibble(num = 1:10)
dat
# A tibble: 10 × 1
     num
   <int>
 1     1
 2     2
 3     3
 4     4
 5     5
 6     6
 7     7
 8     8
 9     9
10    10
dat %>% 
  mutate(kekka = num > 4)
# A tibble: 10 × 2
     num kekka
   <int> <lgl>
 1     1 FALSE
 2     2 FALSE
 3     3 FALSE
 4     4 FALSE
 5     5 TRUE 
 6     6 TRUE 
 7     7 TRUE 
 8     8 TRUE 
 9     9 TRUE 
10    10 TRUE 
dat %>% 
  mutate(kekka = if_else(num > 4, "4より大きい","4以下"))
# A tibble: 10 × 2
     num kekka      
   <int> <chr>      
 1     1 4以下      
 2     2 4以下      
 3     3 4以下      
 4     4 4以下      
 5     5 4より大きい
 6     6 4より大きい
 7     7 4より大きい
 8     8 4より大きい
 9     9 4より大きい
10    10 4より大きい

こんな感じである列の状況に応じた、新しい列をつくることが可能です。

条件にはロジカルベクトルであればなんでもよいので、正規表現で学んだような、

dat <- tibble(
  mixed = c("1","l","5","S","O","0")
)

dat
# A tibble: 6 × 1
  mixed
  <chr>
1 1    
2 l    
3 5    
4 S    
5 O    
6 0    
dat %>% 
  mutate(
    is_num = if_else(str_detect(mixed,"\\d+"),"NUM","ELSE"))
# A tibble: 6 × 2
  mixed is_num
  <chr> <chr> 
1 1     NUM   
2 l     ELSE  
3 5     NUM   
4 S     ELSE  
5 O     ELSE  
6 0     NUM   

のような形で数字のみの文字列とそうでない場合で別々の記載をすることが可能です。

ここまでの例では、TRUE、FALSEの場合、それぞれ単一の結果を返していますが、ここは入力されるロジカルベクトルと同じ長さのベクトルでもよいです。

dat <- tibble(
  num = 1:10,
  alpha = letters[1:10],
  ALPHA = LETTERS[1:10]
)

dat
# A tibble: 10 × 3
     num alpha ALPHA
   <int> <chr> <chr>
 1     1 a     A    
 2     2 b     B    
 3     3 c     C    
 4     4 d     D    
 5     5 e     E    
 6     6 f     F    
 7     7 g     G    
 8     8 h     H    
 9     9 i     I    
10    10 j     J    

こんな表があったとして、numが5以上の場合はalpha列の結果を返したくて、numが5未満の場合はALPHAの結果を返したい場合

dat %>% mutate(kekka = if_else(num>=5, alpha, ALPHA))
# A tibble: 10 × 4
     num alpha ALPHA kekka
   <int> <chr> <chr> <chr>
 1     1 a     A     A    
 2     2 b     B     B    
 3     3 c     C     C    
 4     4 d     D     D    
 5     5 e     E     e    
 6     6 f     F     f    
 7     7 g     G     g    
 8     8 h     H     h    
 9     9 i     I     i    
10    10 j     J     j    

numの値に応じて、kekka列の値が入力されていますね?

if_else関数を利用していて、生じるエラーで代表的なものが型の不一致です。

ベクトルは

c(1,2,"a",3)
[1] "1" "2" "a" "3"

複数の型が混在することができないという話覚えていますか?if_else関数は、もし結果のベクトルに複数の型が入っていた場合に、明確にエラーを返します。

dat <- tibble(
  num = 1:10, 
  cond1 = letters[1:10],
  cond2 = 1:10
)
dat
# A tibble: 10 × 3
     num cond1 cond2
   <int> <chr> <int>
 1     1 a         1
 2     2 b         2
 3     3 c         3
 4     4 d         4
 5     5 e         5
 6     6 f         6
 7     7 g         7
 8     8 h         8
 9     9 i         9
10    10 j        10

こういうデータがあるとして、

dat %>% 
  mutate(result = if_else(num >= 5, cond1, cond2))
Error in `mutate()`:
ℹ In argument: `result = if_else(num >= 5, cond1, cond2)`.
Caused by error in `if_else()`:
! Can't combine `true` <character> and `false` <integer>.

エラーメッセージで、falseはcharacterベクトルでないといけませんと怒られました。なので、

dat %>% 
  mutate(result = if_else(num >= 5, cond1, as.character(cond2)))
# A tibble: 10 × 4
     num cond1 cond2 result
   <int> <chr> <int> <chr> 
 1     1 a         1 1     
 2     2 b         2 2     
 3     3 c         3 3     
 4     4 d         4 4     
 5     5 e         5 e     
 6     6 f         6 f     
 7     7 g         7 g     
 8     8 h         8 h     
 9     9 i         9 i     
10    10 j        10 j     

の様に、明確に同じ型にそろえてあげる必要があります。自動変換してくれてもよさそうに感じるかもしれませんが、自動変換のせいでデータが勝手に欠損したりする可能性もあるため、この動作の方が正解です。

この同じ型ルールで、生じる問題でもう一つ代表的なものが欠損値が生じた場合の取り扱いです。

dat <- tibble(num=c(1,2,3,4))
dat
# A tibble: 4 × 1
    num
  <dbl>
1     1
2     2
3     3
4     4

例えば、このデータでnum列が1以外の時は欠損させたい場合、

dat %>% mutate(kekka = if_else(num==1,1,NA))
# A tibble: 4 × 2
    num kekka
  <dbl> <dbl>
1     1     1
2     2    NA
3     3    NA
4     4    NA

と書くと、エラーが生じます。

これは、

class(NA)
[1] "logical"

がロジカルなため、if_elseの条件で数値型のTrueの場合と型が合致しなかったから生じているエラーです。

これを回避する方法、簡単なのですが、知らないとてこづります。実は、NAには文字列型のNA,数値型のNAなどと、型に対応するNAが用意されているので、それを利用すれば解決です。具体的には、

class(NA_character_)
[1] "character"
class(NA_complex_)
[1] "complex"
class(NA_integer_)
[1] "integer"
class(NA_real_)
[1] "numeric"

を利用してあげればOKです。

dat %>% mutate(kekka = if_else(num==1,1,NA_real_))
# A tibble: 4 × 2
    num kekka
  <dbl> <dbl>
1     1     1
2     2    NA
3     3    NA
4     4    NA

うまくいきましたね?

以上,if_elseの基本的な使い方でした。