[Data Science With R] 10. 범주형 데이터 조작 with forcats (202406)

2023. 4. 8. 20:28·Data Science/Manipulation

범주형 자료는 가질 수 있는 값(범주)이 일반적으로 알려져 있고 고정되어 있는 데이터를 뜻합니다.

성별은 대표적인 범주형 변수로 남자와 여자 두 가지 범주로 구성되어 있습니다.

또한 성별 외에도 혈액형(A/B/AB/O형), 검사결과(음성/양성) 등을 범주형 자료라고 볼 수 있습니다.

마지막으로 키, 연령과 같이 연속형인 변수는 Binning(구간화)를 통해 범주형 데이터로 변형할 수 있습니다.

 

 

R에서는 범주형 데이터를 나타내기 위해 주로 Factor(팩터)를 사용하는데요.

base R에서는 factor(x, levels, labels, ordered) 함수를 이용해서 팩터형으로 변환할 수 있습니다.

이때 levels는 범주가 가질 수 있는 값들을 입력받아 범주에 포함되지 않는 값은 NA로 변환하여 출력합니다.

data <- c("Men", "Men", "Women", "Men", "Women")
A <- factor(data, levels=c("Men", "Women")) 
B <- factor(data, levels=c("Women", "Men"))
sort(A) # Men   Men   Men   Women Women
sort(B) # Women Women Men   Men   Men

 

 

다만 베이스 R에서는 범주형 데이터를 위한 함수들이 부족해서 forcats 라이브러리의 함수를 소개합니다.

  • fct : factor의 엄격한 함수로, levels에 없는 값을 NA로 바꾸지 않고 에러를 발생시킴
    • fct( x = character(), levels = NULL, na = character)
    • levels를 지정하지 않는 경우 처음 등장하는 범주부터 수준(level)를 부여 (* base R은 알파벳 순서로 부여)
x <- c("A", "O", "O", "AB", "A")
factor(x, levels = c("A", "B", "AB"))  # Work!
fct(x, levels = c("A", "B", "AB"))     # Error!

factor(x) # Levels: A AB O
fct(x)    # Levels: A O AB

 

 

  • fct_infreq(f, w = NULL, ordered = NA) : 각 범주 마다의 관측치 수에 따라 수준을 정하는 함수
p1 <- gss_cat |> 
  ggplot(aes(marital)) + geom_bar() + labs(title = "Basic")
p2 <- gss_cat |> 
  ggplot(aes(fct_infreq(marital))) + geom_bar() + labs(title = "fct_infreq", x="marital")
p1 / p2

 

 

  • fct_reorder / fct_reorder2 : 다른 변수의 값을 사용해 범주의 수준을 변경하는 함수
    • fct_reorder(.f, .x, .fun = median, ..., .na_rm=NULL) : 범주의 순서를 .x의 중앙값(.fun)을 사용해 작은 순으로 재정렬
    • fct_reorder2(.f, .x, .y, .fun = last2, ..., na_rm=NULL) : 범주의 순서를 가장 큰 x값과 연관된 y값으로 변경
  • fct_rev(f) : 범주의 순서를 뒤바꾸는 함수
gss_cat |> 
  summarise(mean_age = mean(age, na.rm = T),
            n = n(), .by = "partyid") |> 
  ggplot(mapping=aes(y=partyid, x=mean_age)) + 
  geom_col(fill="white", color = "grey40") + 
  geom_point(color = "red", size = 3) + 
  coord_cartesian(xlim=c(25, 55)) +
  labs(x = "Average Age", y = NULL, title = "Original") -> p1

gss_cat |> 
  summarise(mean_age = mean(age, na.rm = T),
            n = n(), .by = "partyid") |> 
  ggplot(mapping=aes(y=fct_reorder(partyid, mean_age), x=mean_age)) + 
  geom_col(fill="white", color = "grey40") + 
  geom_line(aes(group=1), linewidth = 0.4) +
  geom_point(color = "red", size = 3) +
  coord_cartesian(xlim=c(25, 55)) +
  labs(x = "Average Age", y = NULL, title = "fct_reorder")  -> p2

gss_cat |> 
  summarise(mean_age = mean(age, na.rm = T),
            n = n(), .by = "partyid") |> 
  ggplot(mapping=aes(y=fct_reorder(partyid, mean_age) |> fct_rev(), x=mean_age)) + 
  geom_col(fill="white", color = "grey40") + 
  geom_line(aes(group=1), linewidth = 0.4) +
  geom_point(color = "red", size = 3) + 
  coord_cartesian(xlim=c(25, 55)) +
  labs(x = "Average Age", y = NULL, title = "fct_reorder + fct_rev") -> p3

p1 + p2 + p3

 

 

  • fct_recode(.f, ...) : 범주를 변경하는 함수로 ...에 새값"="기존값"와 같이 named 문자형 벡터를 제공
    • named vector에 언급되지 않은 범주는 그대로 사용됨
    • fct_relabel(.f, .fun, ...)는 문자 조작 함수(stringr와 같은)를 사용해 변경할 범주의 이름을 일괄로 변경
gss_cat |> count(partyid)
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)

 

 

  • fct_collapse(.f, ..., other_level = NULL) : 기존에 존재하는 범주들을 지정된 범주로 변경하는 함수로 다수의 범주를 적은 수의 범주로 병합할때 사용
    • ...에는 named character vector를 지정하여 기존 변수들을 새로운 범주로 변경
fct_collapse(gss_cat$partyid,
    missing = c("No answer", "Don't know"),
    other = "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"))

 

 

  • fct_lump_min(f, min, w=NULL, other_level = "Other") : min보다 적은 관측치를 가지는 범주들을 합치는 함수
  • fct_lump_prop(f, prop, w=NULL, other_level = "Other") : prop * n 보다 적은 관측치를 가지는 범주들을 합치는 함수
  • fct_lump_n(f, n, w = NULL, other_level = "Other", ties.method = "min") : 관측치가 많은 n개의 범주를 제외한 나머지 범주들을 합치는 함수
x <- fct(rep(LETTERS[1:10], times = c(100, 10, 5, 200, 30, 450, 20, 2, 15, 30)))
x |> table()

# fct_lump_min 
x |> fct_lump_min(30) |> table() # 30보다 적은 관측치를 가지는 범주들은 하나로 묶임
x |> fct_lump_min(100)|> table() # 100보다 적은 관측치를 가지는 범주들은 하나로 묶임

# fct_lump_prop
x |> fct_lump_prop(0.10) |> table() # 관측치수가 862 * 0.1 보다 작은 범주는 하나로 묶임 
x |> fct_lump_prop(0.02) |> table() # 관측치수가 862 * 0.02보다 작은 범주는 하나로 묶임

# fct_lump_n
x |> fct_lump_n(1) |> table() # F(450) Other(412)
x |> fct_lump_n(3) |> table() # A(100), D(200), F(450), Other(112)

 

 

  • fct_relevel(.f, ..., after=0L) : 범주의 순서를 앞 n번째로 바꾸는 방법
p1 <- gss_cat |> ggplot(mapping=aes(x=rincome)) + geom_bar() + 
  labs(x=NULL, y=NULL, title = "Original") + theme(axis.text.x = element_text(angle=30)) +
  scale_y_continuous(labels = scales::label_dollar()) 

p2 <- gss_cat |> 
  mutate(rincome = fct_relevel(rincome, "Not applicable", after = 0)) |> 
  ggplot(mapping=aes(x=rincome)) + geom_bar() + 
  labs(x=NULL, y=NULL, title = "fct_relevel - change one level") + theme(axis.text.x = element_text(angle=30)) + 
  scale_y_continuous(labels = scales::label_dollar()) 
  
p3 <- gss_cat |> 
  mutate(rincome = fct_relevel(rincome, "Not applicable", "No answer", "Don't know", "Refused", after = Inf)) |> 
  ggplot(mapping=aes(x=rincome)) + geom_bar() + 
  labs(x=NULL, y=NULL, title = "fct_relevel - change four level") +
  theme(axis.text.x = element_text(angle=30)) +
  scale_y_continuous(labels = scales::label_dollar())

p1 + p2 / p3 + patchwork::plot_layout(widths = c(0.4, 0.6))

 

 

  • fct_c(...) : 서로 다른 factor를 하나의 범주형 데이터로 묶어주는 함수 (수준이 같을 필요는 없음)
facA <- factor(x=c("A", "B", "A", "A")) # levels : A B
facB <- factor(x=c("B", "AB", "AB"))    # levels : AB B
facC <- factor(x=c("O", "O"))           # levels : O

fct_c(facA, facB, facC)          # levels : A B AB O
fct_c(!!!list(facA, facB, facC)) # levels : A B AB O

 

 

  • fct_expand(f, ..., after = Inf) :  범주형 객체에 범주를 추가하는 함수
    • after로 새로운 범주의 수준을 결정할 수 있음
f <- factor(c("A", "A", "B", "A", "AB", "B", "A"))
f                           # levels : A AB B
fct_expand(f, "O")          # levels : A AB B O
fct_expand(f, "O", after=2) # levels : A AB O B

 

 

 

기타 유용한 함수

  • fct_count(f, sort=F, prop=F) : 범주의 개수를 요약해주는 함수
    • sort = T 경우 가장 많은 개수를 가진 범주부터 정렬해서 출력
    • prop = T 경우 특정 범주의 비율을 출력
f<- factor(sample(letters)[rpois(1000, 10)])
table(f)
fct_count(f)
fct_count(f, sort = TRUE)
fct_count(f, sort = TRUE, prop = TRUE)

 

  • fct_drop(f, only = NULL) : 사용하지 않는 범주를 드랍하는 함수 (값이 존재하는 범주는 제거할 수 없음)
    • only : 값이 존재하지 않는 범주 중에서 제거할 범주를 문자형 벡터로 제공
f <- factor(c("a", "b"), levels = c("a", "b", "c"))
f
fct_drop(f) # levels : a b

# Set only to restrict which levels to drop
fct_drop(f, only = "a")  # levels : a b c
fct_drop(f, only = "c")  # levels : a b

 

  • fct_anon(f, prefix = "") : 범주의 수준을 임의의 수치형 값으로 변환하는 함수로 범주의 값이나 범주의 순서가 보존되지 않음
gss_cat$relig %>% 
  fct_count(sort=T) |> slice_head(n=5)
gss_cat$relig %>% fct_anon(prefix = "C") %>%
  fct_count(sort=T) |> slice_head(n=5)

 

  • fct_inorder(f, ordered = NA) : 처음 등장하는 범주부터 첫번째 수준으로 재정렬하는 함수
  • fct_inseq(f, ordered = NA) : 범주 자체의 값으로 수준을 재정렬하는 함수 (범주는 무조건 숫자여야 함)
f <- fct(x=c("3", "1", "3", "2", "1", "1"), levels = 4:1 |> as.character())
f                     # levels : 4 3 2 1
f |> fct_inorder()    # levels : 3 1 2 4
f |> fct_inseq()      # levels : 1 2 3 4

 

  • fct_shuffle(f) : 범주의 수준을 임의로 설정
# fct_shuffle : 범주의 수준을 임의로 설정
f <- fct(x=letters[1:5])
f                  # levels : a b c d e
f |> fct_shuffle() # levels : d a b e c (실행할때마다 변함)

 

 

마지막으로 ggplot2로 작업시 값이 없는 레벨은 제거하는 것을 방지하기 위해 scale_x_discrete(drop=F)를 추가하여 모든 레벨을 보이게 할 수 있습니다.

toy_data <- tibble(
  mon = factor(sample(x=month_level[-2], size = 1000, replace = T),
               levels = month_level))
               
toy_data %>% 
  ggplot(mapping=aes(x=mon)) + geom_bar()
  
toy_data %>% 
  ggplot(mapping=aes(x=mon)) + geom_bar() +
  scale_x_discrete(drop=F)

저작자표시 비영리 변경금지 (새창열림)

'Data Science > Manipulation' 카테고리의 다른 글

[Data Science With R] 12. 함수(Function)  (0) 2023.04.16
[Data Science With R] 11. Time Data with lubridate (202406)  (0) 2023.04.08
[Data Science With R] 9. 정규표현식(Regular Expression) with Stringr (202406)  (0) 2023.04.04
[Data Science With R] 8. 관계형 데이터  (0) 2023.04.01
[Data Science With R] 7. 데이터 변형 with tidyr (202405)  (1) 2023.04.01
'Data Science/Manipulation' 카테고리의 다른 글
  • [Data Science With R] 12. 함수(Function)
  • [Data Science With R] 11. Time Data with lubridate (202406)
  • [Data Science With R] 9. 정규표현식(Regular Expression) with Stringr (202406)
  • [Data Science With R] 8. 관계형 데이터
임파카
임파카
[ML & Statistics] 모바일 버전에서 수식 오류가 있어 PC 환경에서 접속하는 것을 권장합니다.
  • 임파카
    무기의 스탯(Stat)
    임파카
  • 전체
    오늘
    어제
    • Study (151)
      • Data Science (45)
        • Modeling (18)
        • Manipulation (21)
        • Visualization (5)
      • Statistics (60)
        • Mathmetical Statistics (54)
        • Categorical DA (1)
      • Web Programming (17)
      • Marketing (0)
      • AI (26)
        • Machine Learning (16)
        • Deep Learning (10)
      • 활동 및 프로젝트 (3)
  • 인기 글

  • hELLO· Designed By정상우.v4.10.5
임파카
[Data Science With R] 10. 범주형 데이터 조작 with forcats (202406)
상단으로

티스토리툴바