R에서 팩터형은 범주형 변수에 사용되는데, 범주형변수란 가질 수 있는 값이 미리 고정되고 또 알려진 변수를 말한다.팩터형은 문자형 벡터를 알파벳순이 아닌 순서로 표시하고 싶을 때도 이용할 수 있다.
팩터형을 다루기 위해 forcats 패키지를 사용하려는데,이 패키지에는 범주형 변수(팩터형의 다른말)에 적용하는 도구들이 있다 forcats는 tidyverse 핵심 구성원에 포함되어 있지 않기 때문에 명시적으로 로드해야 한다. #아래와 같이 따로 실행
library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.0 --
## √ ggplot2 3.3.2 √ purrr 0.3.4
## √ tibble 3.0.4 √ dplyr 1.0.2
## √ tidyr 1.1.2 √ stringr 1.4.0
## √ readr 1.4.0 √ forcats 0.5.0
## Warning: package 'tibble' was built under R version 4.0.3
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(forcats)
월을 기록한 변수가 있다고 가정하자
x1 <- c("Dec","Apr","Jan","Mar")
x2 <- c("Dec","Apr","Jam","Mar")
sort(x1)
## [1] "Apr" "Dec" "Jan" "Mar"
팩터형을 이용하면 이러한 문제를 모두 해결할 수 있다.팩터형을 생성하기 위해서는 유효한 레벨들의 리스트를 생성하는 것부터 시작해야 한다.
month_levels <- c("Jan","Fed","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")
이제 팩터형을 사용할 수 있다. 그리고 이 레벨 집합에 포함되지 않는 값은 조용히 NA로 변환된다.
y2 <- factor(x2, levels = month_levels)
y2
## [1] Dec Apr <NA> Mar
## Levels: Jan Fed Mar Apr May Jun Jul Aug Sep Oct Nov Dec
경고가 발생되길 원하는 경우에는 readr::parse_factor()를 사용하면 된다.
y2 <- parse_factor(x2, levels = month_levels)
## Warning: 1 parsing failure.
## row col expected actual
## 3 -- value in level set Jam
앞의 levels를 생략하면 데이터로부터 알파벳 순서로 취할 것 이다.
factor(x1)
## [1] Dec Apr Jan Mar
## Levels: Apr Dec Jan Mar
종종 레벨의 순서가 데이터에서 처음으로 등장하는 순서와 일치되길 원할 수 있다. 팩터형 생성 시 레벨을 unique(x)로 설정하거나 사후적으로 fct_inoder()를 사용하면 된다.
f1 <- factor(x1, levels= unique(x1))
f1
## [1] Dec Apr Jan Mar
## Levels: Dec Apr Jan Mar
f2 <- x1 %>% factor() %>% fct_inorder()
f2
## [1] Dec Apr Jan Mar
## Levels: Dec Apr Jan Mar
시카고 대학 독립연구기관인 NORC 에서 장기간 수행한 미국내 설문조사(General Social Survey)의 샘플 데이터이다.수천개의 문항이 있는데, 이중에서도 팩터형과 작업할 때 자주 발생하는 문제를 보여주는 것들을 gss_cat 으로 선택했다.
gss_cat
## # A tibble: 21,483 x 9
## year marital age race rincome partyid relig denom tvhours
## <int> <fct> <int> <fct> <fct> <fct> <fct> <fct> <int>
## 1 2000 Never ma~ 26 White $8000 to ~ Ind,near r~ Protesta~ Souther~ 12
## 2 2000 Divorced 48 White $8000 to ~ Not str re~ Protesta~ Baptist~ NA
## 3 2000 Widowed 67 White Not appli~ Independent Protesta~ No deno~ 2
## 4 2000 Never ma~ 39 White Not appli~ Ind,near r~ Orthodox~ Not app~ 4
## 5 2000 Divorced 25 White Not appli~ Not str de~ None Not app~ 1
## 6 2000 Married 25 White $20000 - ~ Strong dem~ Protesta~ Souther~ NA
## 7 2000 Never ma~ 36 White $25000 or~ Not str re~ Christian Not app~ 3
## 8 2000 Divorced 44 White $7000 to ~ Ind,near d~ Protesta~ Luthera~ NA
## 9 2000 Married 44 White $25000 or~ Not str de~ Protesta~ Other 0
## 10 2000 Married 47 White $25000 or~ Strong rep~ Protesta~ Souther~ 3
## # ... with 21,473 more rows
팩터형이 티블로 저장되면 해당하는 레벨들을 쉽게 볼 수 없게 된다.볼 수 있는 한가지 방법은 count()이다.
gss_cat %>%
count(race)
## # A tibble: 3 x 2
## race n
## <fct> <int>
## 1 Other 1959
## 2 Black 3129
## 3 White 16395
또는 그래프로도 볼 수 있다.
ggplot(gss_cat, aes(race)) +
geom_bar()
기본적으로 ggplot2는 값이 없는 레벨을 제거 한다.다음과 같이 강제적으로 표시하도록 할 수 있다.
ggplot(gss_cat, aes(race)) +
geom_bar() +
scale_x_discrete(drop = FALSE)
이 레벨들은 유효하지만 이 데이터셋에서 나타나지 않는 값을 나타낸다. dplyr 에는 drop 옵션이 아직 없지만 향후 제공될 예정이다. 펙터형으로 작업할 때 자주하는 작업 두가지는 레벨의 순서와 값을 변경하는 것이다.
시각화에서 팩터 레벨의 순서를 변경하는 것이 유용할 때가 종종 있다. 예를 들어 종교에 따른 하루 tv 시청시간의 평균을 탐색하고 싶다고 해보자.
relig_summary <- gss_cat %>%
group_by(relig) %>%
summarize(
age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
)
## `summarise()` ungrouping output (override with `.groups` argument)
ggplot(relig_summary, aes(tvhours, relig)) + geom_point()
전반적인 패턴이 없기 때문에 이 플롯을 해석하기는 어렵다. fct_reorder()를 사용하여 relig의 레벨을 재정렬 해서 개선할 수 있다. fct_reorder()에는 세개의 인수가 있다.
*f:레벨을 수정할 팩터.
*x:레벨을 재정렬하기 위해 사용할 수치형 벡터.
*선택적으로 fun:f의 각 값에 대해 x값이 여러개가 있을때 사용 할 함수. 기본값은 median 이다.
ggplot(relig_summary, aes(tvhours, fct_reorder(relig, tvhours))) +
geom_point()
종교를 재배열하면 ’모름(Don’t know)’ 범주의 사람들이 TV를 훨씬 많이 보고, 힌두교와 다른 동양 종교 사람들이 훨씬 덜 본다는 것을 쉽게 알 수 있다. 좀 더 복잡한 변환을 해야 된다면 aes() 내부보다는 별도의 mutate() 단계에서 변환할 것을 추천한다. 예를 들어 앞의 플롯을 다음과 같이 다시 작성할 수 있다.
relig_summary %>%
mutate(relig = fct_reorder(relig, tvhours)) %>%
ggplot(aes(tvhours, relig)) +
geom_point()
보고된 소긋 레벨에 따라 평균 나이가 어떻게 변화하는지를 보여주는 플롯을 유사하게 만긓어보면 어떨까?
rincome_summary <- gss_cat %>%
group_by(rincome) %>%
summarize(
age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
)
## `summarise()` ungrouping output (override with `.groups` argument)
ggplot(rincome_summary, aes(age, fct_reorder(rincome, age))) + geom_point()
여기에서 레벨을 임의로 재정렬하는 것은 좋은 생각이 아니다. rincome은 이미 원칙 있게 정렬되어 있어서 건드리지 말아야 하기 때문이다. fct_reorder()는 레벨이 임의적으로 정렬된 팩터의 경우에만 사용해야 한다. ’해당없음(Not applicable)’을 다른 특별한 레벨들과 함께 앞으로 가져오는 것이 좋다. fct_relevel()을 사용하면 된다. 팩터형 f와 앞으로 옮기고자 하는 레벨을 입력하면 된다.
ggplot(rincome_summary, aes(age, fct_relevel(rincome, "Not applicable"))) +
geom_point()
재정렬이 유용한 경우가 있는데, 플롯의 선에 색상을 입힐 때이다. fct_reorder2()는 가장 큰x값과 연관된 y값으로 팩터형을 재정렬한다. 선 색상은 범례와 정렬되므로 이렇게 하면 플롯 읽기가 쉬워진다.
by_age <- gss_cat %>%
filter(!is.na(age)) %>%
count(age, marital) %>%
group_by(age) %>%
mutate(prop = n / sum(n))
ggplot(by_age, aes(age, prop, colour = marital)) +
geom_line(na.rm = TRUE)
ggplot(by_age, aes(age, prop, colour = fct_reorder2(marital, age, prop))) +
geom_line() +
labs(colour = "marital")
fct_infreq()를 사용해 빈도 오름차순으로 레벨을 정렬할 수 있다. 추가 변수가 필요 없어서 재정렬 방법중 가장 간단한 유형이다. fct_rev()와 조합하여 사용할 수 있다.
gss_cat %>%
mutate(marital = marital %>% fct_infreq() %>% fct_rev()) %>% ggplot(aes(marital)) +
geom_bar()
레벨의 순서 변경보다 값을 변경하는 게 강력한 방법이다. 화면출력시 라벨을 명확히 할 수 있고, 레벨을 병합하여 상위 레벨 시각화를 할 수 있다. fct_recode가 일반적이며 각레벨값을 변경할 수 있다. 예를 들어 gss_cat$partyid를보자.
gss_cat %>% count(partyid)
## # A tibble: 10 x 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
이레벨들을 풀어쓰고 병렬구조를 사용해보자
gss_cat %>%
mutate(partyid = fct_recode(partyid,
"Republican, strong" = "Strong republican",
"Republican, weak" = "Not str republican",
"Independent, near rep" = "Ind,near rep",
"Independent, near dem" = "Ind,near dem",
"Democrat, weak" = "Not str democrat",
"Democrat, strong" = "Strong democrat"
)) %>%
count(partyid)
## # A tibble: 10 x 2
## partyid n
## <fct> <int>
## 1 No answer 154
## 2 Don't know 1
## 3 Other party 393
## 4 Republican, strong 2314
## 5 Republican, weak 3032
## 6 Independent, near rep 1791
## 7 Independent 4119
## 8 Independent, near dem 2499
## 9 Democrat, weak 3690
## 10 Democrat, strong 3490
fct_recode()명시적으로 언급되지 안은 레벨은 그대로둔다. 존재하지 않은 레벨을 참조하면 경고가 발생한다. 그룹을 결합하려면 레벨들을 같은 새로운 레벨로 할당하면된다
gss_cat %>%
mutate(partyid = fct_recode(partyid,
"Republican, strong" = "Strong republican",
"Republican, weak" = "Not str republican",
"Independent, near rep" = "Ind,near rep",
"Independent, near dem" = "Ind,near dem",
"Democrat, weak" = "Not str democrat",
"Democrat, strong" = "Strong democrat",
"Other" = "No answer",
"Other" = "Don't know",
"Other" = "Other party"
)) %>%
count(partyid)
## # A tibble: 8 x 2
## partyid n
## <fct> <int>
## 1 Other 548
## 2 Republican, strong 2314
## 3 Republican, weak 3032
## 4 Independent, near rep 1791
## 5 Independent 4119
## 6 Independent, near dem 2499
## 7 Democrat, weak 3690
## 8 Democrat, strong 3490
서로 같지 않은 범주들을 함께 묶는다면 잘 못된 결과를 도출하게 될 것이다. 다수의 레벨을 병합하고자 하면 fct_recofe()의 변형 함수인 fct_collapse()가 편리하다. 각각의 새로운 변수에 대해 이전 레벨로 이루어진 벡터를 제공해야 한다.
gss_cat %>%
mutate(partyid = fct_collapse(partyid,
other = c("No answer", "Don't know", "Other party"),
rep = c("Strong republican", "Not str republican"),
ind = c("Ind,near rep", "Independent", "Ind,near dem"),
dem = c("Not str democrat", "Strong democrat")
)) %>%
count(partyid)
## # A tibble: 4 x 2
## partyid n
## <fct> <int>
## 1 other 548
## 2 rep 5346
## 3 ind 8409
## 4 dem 7180
가끔은 플롯이나 테이블을 간단히 만들기 위해 소규모 그룹 모두를 묶고 싶을 수도 있다. 이때 fct_lump()가 이작업을 한다.
gss_cat %>%
mutate(relig = fct_lump(relig)) %>%
count(relig)
## # A tibble: 2 x 2
## relig n
## <fct> <int>
## 1 Protestant 10846
## 2 Other 10637
기본동작은 묶은 그룹이 가장 작은 그룹이 되는 조건을 유지하면서 작은 그룹 들을 점진적으로 묶는다. N 인수를 사용하여 유지하고 싶은 그룹 개수(other 제외)를 지정할 수 있다.
gss_cat %>%
mutate(relig = fct_lump(relig, n = 10)) %>%
count(relig, sort = TRUE) %>%
print(n = Inf)
## # A tibble: 10 x 2
## relig n
## <fct> <int>
## 1 Protestant 10846
## 2 Catholic 5124
## 3 None 3523
## 4 Christian 689
## 5 Other 458
## 6 Jewish 388
## 7 Buddhism 147
## 8 Inter-nondenominational 109
## 9 Moslem/islam 104
## 10 Orthodox-christian 95