'not a logical vector Call `rlang::last_error()` to see a backtrace'에 해당되는 글 1건

  1. 2020.02.23 [R dplyr] 여러개의 if, else if 조건절을 벡터화해서 처리해주는 case_when() 함수 6

이번 포스팅에서는 R dplyr 패키지의 case_when() 함수를 이용해서 연속형 변수를 여러개의 범주로 구분하여 범주형 변수를 만들어보겠습니다. dplyr 패키지의 case_when() 함수를 사용하면 여러개의 if, else if 조건절을 사용하지 않고도 벡터화해서 쉽고 빠르게 처리를 할 수 있습니다. R dplyr 의 case_when() 함수는 SQL의 case when 절과 유사하다고 보면 되겠습니다. 




간단한 예제로 1~10 까지의 양의 정수를 "2 이하", "3~5", "6~8", "9 이상" 의 4개 범주로 구분을 해보겠습니다. 

(dplyr::case_when()에서 dplyr:: 는 생략해도 되며, dplyr 패키지의 함수를 이용하다는 의미입니다)


case_when(

조건 ~ 할당값, 

조건 ~ 할당값, 

TRUE ~ 할당값)

의 형식으로 작성합니다. 


아래의 예에서는 조건절이 총 4개 사용되었는데요, if, else if, else if, else 등의 조건절문 없이 case_when() 함수의 괄호안에 바로 조건을 나열했고, 마지막에는 앞의 조건절에 모두 해당 안되는 나머지(else)에 대해서 TRUE ~ "9~" 로 지정을 해주었습니다. 



library(dplyr)


x <- 1:10

x


[1]  1  2  3  4  5  6  7  8  9 10



dplyr::case_when(

  x <= 2 ~ "~2",

  x <= 5 ~ "3~5",

  x <= 8 ~ "6~8",

  TRUE ~ "9~"

)


 [1] "~2"  "~2"  "3~5" "3~5" "3~5" "6~8" "6~8" "6~8" "9~"  "9~" 





이때 조건절의 순서가 중요합니다. 복수의 조건절을 나열하면 앞에서 부터 순서대로(in order) 조건에 해당하는 관측치에 대해 값을 할당하게 됩니다. 따라서 만약 TRUE ~ "9~"를 case_when(() 조건절의 제일 앞에 사용하게 되면 1~10까지의 모든 값에 대해 "9~" 를 할당하게 됩니다. 따라서 조건절의 처리 순서를 반드시 고려해서 조건절을 작성해줘야 합니다. 



# order matters!!!

case_when(

  TRUE ~ "9~",

  x <= 2 ~ "~2",

  x <= 5 ~ "3~5",

  x <= 8 ~ "6~8",

)


[1] "9~" "9~" "9~" "9~" "9~" "9~" "9~" "9~" "9~" "9~"

 




case_when() 조건절의 오른쪽(right hand side)의 데이터 유형이 모두 동일해야 합니다. 만약 데이터 유형이 다를 경우 error를 발생합니다. 가령, 아래 예에서는 오른쪽에 character를 반환하게끔 되어있는데 logical 인 NA 가 포함되는 경우 Error가 발생합니다. 이때는 'NA_character_' 를 사용해서 NA가 character로 반환되게끔 해주면 됩니다. 


  • 오른쪽에 문자형(character) 반환하는 경우 NA 값으로는 NA_character_ 사용

 잘못된 사용 예 (오른쪽 데이터 유형 다름)

 올바른 사용 예 (오른쪽 데이터 유형 같음)


# error as NA is logical not character

case_when(

  x <= 2 ~ "~2",

  x <= 5 ~ "3~5",

  x <= 8 ~ "6~8",

  TRUE ~ NA

)


Error: must be a character vector, not a logical vector

Call `rlang::last_error()` to see a backtrace


# use NA_character_

case_when(

  x <= 2 ~ "~2",

  x <= 5 ~ "3~5",

  x <= 8 ~ "6~8",

  TRUE ~ NA_character_

)


[1] "~2"  "~2"  "3~5" "3~5" "3~5" "6~8" "6~8" "6~8" NA    NA 



  • 오른쪽에 숫자형(numeric)을 반환하는 경우 NA 값으로는 NA_real_ 사용

  잘못된 사용 예 (오른쪽 데이터 유형 다름)

 올바른 사용 예 (오른쪽 데이터 유형 같음)


# error as NA is logical not numeric

case_when(

  x <= 2 ~ 2,

  x <= 5 ~ 5,

  x <= 8 ~ 8,

  TRUE ~ NA

)


Error: must be a double vector, not a logical vector

Call `rlang::last_error()` to see a backtrace


# use NA_real_

case_when(

  x <= 2 ~ 2,

  x <= 5 ~ 5,

  x <= 8 ~ 8,

  TRUE ~ NA_real_

)


[1]  2  2  5  5  5  8  8  8 NA NA




dplyr의 case_when() 함수는 mutate() 함수와 함께 사용하면 매우 강력하고 편리하게 여러개의 조건절을 사용해서 새로운 변수를 만들 수 있습니다. 아래는 mtcars 데이터셋의 cyl (실린더 개수)  와 hp (자동차 마력) 의 두 개 변수를 사용해  첫번째 "or" 조건절로 "big" 유형으로 찾고, 두번째 "and" 조건절로 "medium" 유형을 찾으며, 마지막으로 나머지에 대해서는 "small" 유형을 명명해본 예입니다. 



mtcars$name <- row.names(mtcars)


mtcars %>% 

  select(name, mpg, cyl, hp) %>% 

  mutate(

    type = case_when(

      cyl >= 8 | hp >= 180 ~ "big",          # or

      cyl >= 4 & hp >= 120 ~ "medium", # and

      TRUE ~ "small"

    )

  )


                 name  mpg cyl  hp   type

1            Mazda RX4 21.0   6 110  small

2        Mazda RX4 Wag 21.0   6 110  small

3           Datsun 710 22.8   4  93  small

4       Hornet 4 Drive 21.4   6 110  small

5    Hornet Sportabout 18.7   8 175    big

6              Valiant 18.1   6 105  small

7           Duster 360 14.3   8 245    big

8            Merc 240D 24.4   4  62  small

9             Merc 230 22.8   4  95  small

10            Merc 280 19.2   6 123 medium

---- 이하 생략 ----

 




위에서 R dplyr의 case_when() 함수로 진행했던 내용을 PostgreSQL, Greenplum DB에서 하려면 SQL CASE WHEN 문을 아래처럼 사용하면 됩니다. 참고하세요. 



-- PostgreSQL CASE WEHN


SELECT 

   name, 

   mpg, 

   cyl, 

   hp, 

   CASE 

      WHEN (cyl >= 8) OR (hp >= 180) THEN "big"

      WHEN (cyl >= 4) AND (hp >= 120) THEN "median"

      ELSE "small"

   END AS type

FROM mtcars

 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. 



728x90
반응형
Posted by Rfriend
,