-
Handling Factor Variable using forcats package데이터 2022. 8. 7. 21:22
forcats 패키지를 활용한 Factor형 변수 다루기
Photo by Jari Hytönen on Unsplash 범주형 변수 또는 팩터형 변수를 정교하게 다루는 것은 데이터 전처리 및 가시화 등에서 중요한 작업이다. 막대 그래프를 출력했는데 막대의 순서가 크기 순서가 아닌 원래 팩터변수의 레벨순으로 출력되어 보기 싫다든가, 또는 과도하게 다양한 팩터 레벨을 단순화 시키고 싶다는가 하는 경우에 팩터형 변수를 정교하게 다룰 필요가 있다. 팩터형 변수를 다루기 쉽도록 할 목적으로 제작된 패키지가 forcats 패키지이며, forcats 패키지는 tidyverse 생태계를 구성하는 패키지 중 하나로 tidyverse 패키지를 업로드하면 자동으로 업로드된다. 여기서는 forcats 패키지의 다양한 함수를 이용하여 팩터형 변수 다루는 법을 알아본다.
팩터형 변수의 레벨 이해하기
먼저 다음과 같은 경우를 생각해보자. x1_char로 월 이름 벡터를 생성하고 factor()함수를 이용해 팩터형 변수로 변환해보자. 우리는 달 이름을 순서대로 입력한 다음 이를 팩터형으로 변환을 했으니 레벨도 순서대로 되어있을 거라고 생각 할 수 있다. 그러나 levels() 함수로 x1_fct변수의 레벨을 확인해보면 우리의 기대와 다르게 레벨이 알파벳 순서로 지정되었음을 알 수 있다. 월별로 현황을 출력하는데 알파벳 순서로 출력된다면 결과가 이상하게 보일 것이다. 팩터형 변수를 처리 할 때는 항상 레벨을 고려하면서 데이터를 처리해야 한다.
x1_char <- c("Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec") x1_fct <- factor(x1_char) levels(x1_fct)
[1] "Apr" "Aug" "Dec" "Feb" "Jan" "Jul" "Jun" "Mar" "May" "Nov" "Oct" "Sep"
x1 변수의 레벨을 factor()함수의 levels 인자를 통해 순서대로 레벨을 지정해 보자.
x2 <- factor(x1_fct, levels = x1_char) levels(x2)
[1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
이제 팩터형 변수 x2의 레벨이 캘린더의 순서대로 지정 되었음을 확인할 수 있다.
팩터형 변수의 실습을 위한 데이터
팩터형 변수로 실습하기 알맞은 데이터로 구체적인 실습을 진행하자. gss_cat 데이터는 2000년 부터 2014년까지 미국의 사회조사 데이터로 다양한 팩터형 변수가 포함되어 팩터형 변수 실습을 해 볼 수 있는 좋은 데이터이다.
data(gss_cat) glimpse(gss_cat)
Rows: 21,483 Columns: 9 $ year <int> 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 20… $ marital <fct> Never married, Divorced, Widowed, Never married, Divorced, Mar… $ age <int> 26, 48, 67, 39, 25, 25, 36, 44, 44, 47, 53, 52, 52, 51, 52, 40… $ race <fct> White, White, White, White, White, White, White, White, White,… $ rincome <fct> $8000 to 9999, $8000 to 9999, Not applicable, Not applicable, … $ partyid <fct> "Ind,near rep", "Not str republican", "Independent", "Ind,near… $ relig <fct> Protestant, Protestant, Protestant, Orthodox-christian, None, … $ denom <fct> "Southern baptist", "Baptist-dk which", "No denomination", "No… $ tvhours <int> 12, NA, 2, 4, 1, NA, 3, NA, 0, 3, 2, NA, 1, NA, 1, 7, NA, 3, 3…
팩터형 변수 레벨의 순서를 조정
팩터형 변수의 레벨 순서를 조정하는 경우는 경우는 대게 다음과 같은 경우이다.
- 팩터형 변수의 빈도 순으로 출력 결과 조정
- 다른 변수의 값에 따라 팩터형 변수의 레벨 순서 조정
- 레벨 순서를 역순으로 조정
gss_cat 데이터의 종교(relig)별 인원수(빈도)에 대한 막대그래프를 그려보고 팩터형 변수의 레벨 조정 필요성을 느껴보자.
gss_cat %>% group_by(relig) %>% ggplot(aes(x = relig)) + geom_bar() + labs(x = "Religion", y = "Count") + coord_flip()
코드를 보면 relig 변수로 그룹핑하여 geom_bar()이용해 종교별 빈도수 막대 그래프를 출력하였다. 하지만 그래프 모양이 마음에 들지 않는다. 출력된 순서가 relig 변수의 레벨순으로 출력되었기 때문이다.
이번에는 출력 순서를 relig 변수의 빈도 순서로 조정해서 출력해보자. 이때 사용하는 함수가 fct_infreq() 함수이다. 즉 팩터형 변수의 레벨 종류별 빈도를 기준으로 레벨을 조정하는 방법이다. 이런 경우 전체 데이터의 relig 변수의 레벨을 바꿀 필요는 없고 ggplot 함수의 aes 인자의 레벨만 빈도순으로 바꾸어 출력하는 것이 수월하다.gss_cat %>% group_by(relig) %>% ggplot(aes(x = fct_infreq(relig))) + # 빈도순으로 지정 geom_bar() + labs(x = "Religion", y = "Count") + coord_flip()
그래프는 relig 변수의 빈도순으로 정렬되어 출력되었다.
출력의 순서를 거꾸로 조정할 때는 fct_rev()를 사용하면된다.gss_cat %>% group_by(relig) %>% ggplot(aes(x = fct_rev(fct_infreq(relig)))) + # 빈도의 역순으로 geom_bar() + labs(x = "Religion", y = "Count") + coord_flip()
이번에는 relig별 평균 TV 시청시간을 그래프로 그려보자. gss_cat 데이터를 종교별로 그룹화여 평균 시청시간을 계산하고 이를 막대그래프로 그려야 한다.
gss_cat %>% drop_na(tvhours) %>% # tvhour 변수의 결측치가 있으면 제거 group_by(relig) %>% summarise(tvhours = mean(tvhours)) %>% ggplot(aes(x = relig, y = tvhours)) + geom_col() + labs(x = "Religion", y = "TV hours") + coord_flip()
각 종교별 평균 TV 시청시간의 막대 그래프가 relig 변수의 레벨순으로 출력되었다. relig변수의 레벨을 평균 TV 시청시간(tvhours) 변수의 크기순으로 정렬 시켜서 출력하고 싶다. 팩터형 변수의 레벨 순서를 다른 변수의 값 순서로 재조정 할때 사용하는 함수가 fct_reorder() 함수이다. 소스코드에 적용된 fct_reorder(relig, tvhours) 함수의 의미는 “팩터형 변수 relig의 레벨을 숫자형 변수 tvhours 값의 순서로 재조정 하라”라는 의미이다.
gss_cat %>% drop_na(tvhours) %>% group_by(relig) %>% summarise(tvhours = mean(tvhours)) %>% mutate(relig = fct_reorder(relig, tvhours)) %>% ggplot(aes(x = relig, y = tvhours)) + geom_col() + labs(x = "Religion", y = "TV hours") + coord_flip()
그래프가 우리가 원하는 대로 TV 시청시간 순으로 출력되었다. 소스의 mutate(relig = fct_reorder(relig, tvhours))를 주목하자. 요약된 tibble 데이터의 relig 변수의 레벨을 tvhours 값으로 재조정한 후 이를 ggplot의 aes 매핑 인자로 입력하였다. 하지만 아래 코드와 같이 gglplot의 aes의 x 인자에 fct_reorder()함수를 바로 적용해도 된다.
gss_cat %>% drop_na(tvhours) %>% group_by(relig) %>% summarise(tvhours = mean(tvhours)) %>% ggplot(aes(x = fct_reorder(relig, tvhours), y = tvhours)) + geom_col() + labs(x = "Religion", y = "TV hours") + coord_flip()
팩터형 변수 레벨을 재 지정
팩터 레벨이 지나치게 세분화 되어있거나 분석에 필요한 구분 보다 더 상세하게 되어 있는 경우 우리는 레벨을 우리가 원하는 대로 재 지정해서 사용할 수 있다. 다음의 사례를 보자.
gss_cat 데이터의 partyid에는 지지 정당이 들어있다. 먼저 지지 정당에는 어떤 종류들이 있는지 알아보자.gss_cat %>% count(partyid)
# A tibble: 10 × 2 partyid n <fct> <int> 1 No answer 154 2 Don't know 1 3 Other party 393 4 Strong republican 2314 5 Not str republican 3032 6 Ind,near rep 1791 7 Independent 4119 8 Ind,near dem 2499 9 Not str democrat 3690 10 Strong democrat 3490
지지정당(partyid) 종류(levels)에는 무응답(No answer) 이나 모름(Don’t know) 또는 기타(Other party)와 같은 모호한 응답들이 포함되어 있다. 레벨을 단순화 하기 위해 이들을 통합해서 전부 기타라는 의미를 갖는 “Other”라는 레벨로 조정 할 수 있다. 이때 사용하는 함수가 fct_recode()함수이다. fct_recode() 함수는 팩터 레벨을 우리가 원하는 대로 조정할때 사용한다.
gss_cat %>% mutate(partyid = fct_recode(partyid, "Other" = "No answer", "Other" = "Don't know", "Other" = "Other party")) %>% select(partyid) %>% count(partyid)
# A tibble: 8 × 2 partyid n <fct> <int> 1 Other 548 2 Strong republican 2314 3 Not str republican 3032 4 Ind,near rep 1791 5 Independent 4119 6 Ind,near dem 2499 7 Not str democrat 3690 8 Strong democrat 3490
기존의 팩터 레벨이었던 무응답(No answer), 모름(Don’t Know), 기타 당(Other party) 등이 Other로 통합되어 레벨이 8가지로 줄었음을 확인 할 수 있다. 그런데 지지정당에 Strong, Not str, near, Strong 등의 수식어를 붙여 과도하게 구분 되어 있음을 알 수 있다. 이를 단순화하여 “Republican”, “Independent”, Democrat”으로 대분류 하고 싶을 때, 즉 레벨을 흐트려 재그룹 하고자 할 때 유용한 함수가 fct_collapse()함수 이다.
gss_cat %>% mutate(partyid = fct_collapse(partyid, "other" = c("Other party","No answer","Don't know" ), "rep" = c("Strong republican","Not str republican"), "dem" = c("Not str democrat","Strong democrat","Ind,near dem"), "ind" = c("Ind,near rep","Independent"))) %>% select(partyid) %>% count(partyid)
# A tibble: 4 × 2 partyid n <fct> <int> 1 other 548 2 rep 5346 3 ind 5910 4 dem 9679
이제 정당 지지도가 4가지로 단순화 되었음을 확인할 수 있다.
이번에는 위에서 그렸던 종교별(relig) 신자수 그래프를 종으로 그린 다음 그래프를 살펴보자.
gss_cat %>% group_by(relig) %>% ggplot(aes(x = fct_infreq(relig))) + # relig 변수의 빈도순으로 geom_bar() + labs(x = "Religion", y = "Count")
이 그래프의 문제가 무엇인가? 종교의 종류가 많아 x축의 레이블들이 중첩되어 내용을 식별 할 수 없다. 이런 종류의 문제를 해결하는 방법은 몇가지가 있다. 예를 들어 위에서 본 바와 같이 coord_flip() 함수를 사용하여 x축과 y축을 바꾸어 종으로 그리는 방법, 또는 x축의 레이블을 회전시켜 수직으로 출력함으로써도 해결할 수도 있다.
gss_cat %>% group_by(relig) %>% ggplot(aes(x = fct_infreq(relig))) + # 빈도순 geom_bar() + labs(x = "Religion", y = "Count") + coord_flip()
gss_cat %>% group_by(relig) %>% ggplot(aes(x = fct_infreq(relig))) + # 빈도순 geom_bar() + labs(x = "Religion", y = "Count") + theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5 ))
그러나 여기서 사용한 두가지 방법은 빈도수가 적은 종교들까지 모두 나열되어있다. 실제로는 모든 팩터의 레벨을 다 표시하여 그래프를 복잡하게 하기 보다는 주요 상위 레벨은 각각 표시하고 나머지는 묶어서 Other 이렇게 표현 하는 것이 유리한 경우가 많다. 이런 경우 유용하게 사용할 수 있는 함수가 fct_lump()함수이다. fct_lump() 함수는 존치 시키고 싶은 레벨의 갯수를 n 인자로 지정하고 나머지는 “Others”로 한꺼번에 묶어서 표현 해준다. 다음은 fct_lump를 사용하여 팩터 레벨을 상위 3개, 상위 5개 만을 레벨로 인정하고 나머지는 “Other” 레벨로 묶은 예이다.
gss_cat %>% mutate(relig = fct_lump(relig, n = 3)) %>% # 상위 세개만 남기고 나머진 other select(relig) %>% count(relig) # A tibble: 4 × 2 relig n <fct> <int> 1 None 3523 2 Catholic 5124 3 Protestant 10846 4 Other 1990 gss_cat %>% mutate(relig = fct_lump(relig, n = 5)) %>% # 상위 다섯개 남기고 나머진 other select(relig) %>% count(relig) # A tibble: 6 × 2 relig n <fct> <int> 1 Christian 689 2 None 3523 3 Jewish 388 4 Catholic 5124 5 Protestant 10846 6 Other 913
조정된 팩터 레벨을 적용하여 막대 그래프를 그려보자. relig 변수의 빈도수 기준 상위 세개의 레벨에 대한 그래프를 그리면 다음과 같다.
gss_cat %>% mutate(relig = fct_lump(relig, n = 3)) %>% # 상위 세개 남기고 나머진 other group_by(relig) %>% ggplot(aes(x = fct_infreq(relig))) + geom_bar() + labs(x = "Religion", y = "Count")
이 그래프를 통해 응답자의 relig 빈도 순서는 개신교, 카톨릭 및 무교 순위이고 기타 종교의 합이 무교 보다 적음을 알 수 있다. 팩터의 레벨이 과도하게 많은 경우 분석의 목적에 따라 단순화 시켜 사용하는 것도 분석의 요점을 명확하게 할 수 있는 방법이다.
팩터형 변수의 레벨 제거
조사 대상의 인종을 나타내는 race 변수의 레벨을 살펴보자. level의 종류를 확인할때는 levels() 함수를 이용한다.
data(gss_cat) levels(gss_cat$race)
[1] "Other" "Black" "White" "Not applicable"
race 변수의 레벨은 “Other” , “Black”, “White”, “Not applicable” 네가지 임을 알 수 있다. 그렇다면 race 변수의 레벨별 빈도는 어떻게 될까?
gss_cat %>% count(race)
# A tibble: 3 × 2 race n <fct> <int> 1 Other 1959 2 Black 3129 3 White 16395
그런데 race변수의 레벨별 빈도를 확인해보니 Not applicable은 응답자가 없음을 알수 있다. 즉 기타를 의미하는 “Other”외에 “Not applicable” 이란 레벨이 존재하고는 있지만 그 문항을 선택한 응답자가 없다는 뜻이고 이는 결측치와는 다른 것이다. 다음과 같이 결측치를 확인해보면 결측치는 없음을 알 수 있다.
sum(is.na(gss_cat$race))
[1] 0
이런 Not applicable이란 분석에 불필요한 레벨을 제거하고자 할 때는 fct_drop()을 이용한다.
gss_cat <- gss_cat %>% mutate(race = fct_drop(race)) levels(gss_cat$race)
[1] "Other" "Black" "White"
지금까지 다양한 상황에서의 팩터형 데이터 처리하는 방법들을 사례와 함께 알아보았다. 팩터형 변수를 다양하게 다루는 능력은 데이터 전처리의 중요한 부분이다. 오늘 배운 forcats 패키지의 주요 함수는 다음과 같다.
- fct_infreq()
- fct_rev()
- fct_reorder()
- fct_recode()
- fct_collapse()
- fct_lump()
- fct_drop()
'데이터' 카테고리의 다른 글
CARET을 활용한 회귀 모형 (0) 2022.09.04 탐색적 데이터분석 : EDA(Explatory Data Analysis) (0) 2022.08.21 Tidymodels를 활용한 ML Process (0) 2022.07.23 ggplot을 이용한 데이터가시화 II (0) 2022.07.08 ggplot을 이용한 데이터 가시화 I (0) 2022.06.26