1 요인(factor) 기초 1

요인형 자료형을 문자형 대신 사용하게 되면 두가지 장점이 있는데, 가장 큰 장점은 아마도 오탈자로 생기는 새로운 유형 생성을 억제시킬 수 있다. 즉, 월은 12개월 밖에 존재하지 않는다. 따라서, 오탈자로 인해 범주가 추가로 생성되는 것을 사전에 방지할 수 있다. 그리고, 요인형을 자료형을 갖추게 되면 정렬을 쉽게 할 수도 있다.

일반 문자형 벡터을 정렬하게 되면 알파벳순으로 정렬하는데 무의미하다.

[1] "Apr" "Dec" "Jan" "Mar"

levels 인자로 수준을 정의하게 되면 오탈자로 인한 엉뚱한 범주가 추가되는 것을 방지하는 것과 더불어 정렬하게 되면 사람에게 좀더 의미있게 다가온다.

[1] Dec Apr Jan Mar
Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
[1] Jan Mar Apr Dec
Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

x2 문자열 벡터에 Jam 범주가 들어가 있는데 이를 levels를 통해 범주를 설정하게 되면 오류를 방지할 수 있다. 특히, parse_factor 명령어를 사용하면 어디가 잘못되었는지 확인이 쉽다.

[1] Dec  Apr  <NA> Mar 
Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

levels를 생략하게 되면 기본디폴트 설정으로 알파벳순으로 범주를 생성하여 수준을 정해버린다.

[1] Dec Apr Jan Mar
Levels: Apr Dec Jan Mar

수준(levels) 순서를 맞추려면 unique() 혹은 fct_inorder() 함수를 사용한다.

[1] Dec Apr Jan Mar
Levels: Dec Apr Jan Mar
[1] Dec Apr Jan Mar
Levels: Dec Apr Jan Mar
[1] "Dec" "Apr" "Jan" "Mar"

2 요인 자료형 다루기 2

요인(factor, 팩터) 자료형을 다룬다는 것은 다음을 의미한다. 하지만, Base Rtidyverse 두가지 방식이 있어 두가지 구문에 대한 이해도 필요하다.

  • 요인 수준(factor level)에 대한 라벨(label) 바꿈 → forcats::fct_recode, dplyr::recode
  • 요인 수준 순서 바꾸기 → fct_relevel()
  • 요인 범주수준을 줄이기 → if_else()
  • 파생 요인 변수 생성시킴 → case_when()

2.1 요인 수준별로 라벨 변경

요인 수준/범주별로 라벨 변경

요인 수준/범주별로 라벨 변경

Base R에서 먼저 요인(factor)에 대한 데이터 분석 작업은 levels() 함수로 범주를 확인하고 summary() 함수로 시작된다.

[1] "Keeping house"    "No answer"        "Other"           
[4] "Retired"          "School"           "Temp not working"
[7] "Unempl, laid off" "Working fulltime" "Working parttime"
   Keeping house        No answer            Other          Retired 
             263                2               76              460 
          School Temp not working Unempl, laid off Working fulltime 
              90               40              104             1230 
Working parttime             NA's 
             273                2 

요인 수준을 다른 형태로 조정하고 싶은데 levels(GSS$LaborStatus)[1:5] 앞에서 5개 범주는 그대로 둔 채로 나머지만 바꾸고자 하는 경우 다음과 같이 코드를 작성한다. 하지만, 이렇게 작성하는 것은 소프트웨어가 부서지기 쉬운(fragile software)의 전형적인 예가 된다.

          Keeping house               No answer                   Other 
                    263                       2                      76 
                Retired                  School Temporarily not working 
                    460                      90                      40 
   Unemployed, laid off       Working full time       Working part time 
                    104                    1230                     273 
                   NA's 
                      2 

또다른 방식으로 다소 코드가 길어지기는 하지만 강건한 방식으로 다음과 같이 코드를 작성할 수 있다. 순서가 아닌 범주가 맞는 경우만 라벨을 바꿔주는 것이라 강건하지만 데이터프레임(GSS)이 여러번, 변수(LaborStatus)도 중복되어 그다지 읽기 즐거운 코드는 아니다.

          Keeping house               No answer                   Other 
                    263                       2                      76 
                Retired                  School Temporarily not working 
                    460                      90                      40 
   Unemployed, laid off       Working full time       Working part time 
                    104                    1230                     273 
                   NA's 
                      2 

dplyr 팩키지 recode() 함수, 혹은 forcats 팩키지 fct_recode()를 사용해서 범주명을 다른 명칭으로 바꿀 수 있는데 코드가 깔끔한다.

# A tibble: 10 x 2
   tidyLaborStatus         n
   <fct>               <int>
 1 Working fulltime     1230
 2 Retired               460
 3 "Working parttime "   273
 4 Keeping house         263
 5 Unempl, laid off      104
 6 School                 90
 7 Other                  76
 8 Temp not working       40
 9 No answer               2
10 <NA>                    2

trimws() 함수 사용

범주가 동일하나 공백문자(whitespace)가 포함된 경우 수작업을 일일이 손대는 대신에 trimws() 함수를 사용하면 수월히 범주를 잘 정리할 수 있다.

[1] "male"    "male "   "male  "  "male   "
[1] "male"

2.2 요인 수준 순서 변경

요인 수준/범주별 순서 변경

요인 수준/범주별 순서 변경

그 다음으로 요인 수준 범주 순서를 바꾸는 것이 많이 사용되는 기능이다. Base R에서는 알파벳 순으로 요인 수준 범주를 기본디폴트 설정되어 있다.

    Above average           Average     Below average        Don't know 
              483              1118               666                21 
Far above average Far below average         No answer              NA's 
               65               179                 6                 2 

상기 순서를 다음과 같이 바꾸어 보자. 즉 소득에 대해서 순위형 범주로 순서를 넣어 정렬시키고자 한다.

Far above average → Above average → Average → Below Average → Far below average → Don’t know → No answer → NA’s

factor() 함수 내부에 levels = 인자조정을 통해서 범주 순서를 재조정시킬 수 있다. 이와 같이 구문을 작성할 경우 Average의 경우 공백 때문에, Below Average는 대소문자로 인해 잘못 범주 구분이 되어 숫자가 맞지 않는다.

Far above average     Above average          Average      Below Average 
               65               483                 0                 0 
Far below average        Don't know         No answer              NA's 
              179                21                 6              1786 

소득에 대해서 순위형 범주로 순서를 넣어 정렬시키고자 할 때 다음과 같이 엽기(?)적으로 코딩을 할 수 있다.

원데이터 범주 순서

    Above average           Average     Below average        Don't know 
              483              1118               666                21 
Far above average Far below average         No answer              NA's 
               65               179                 6                 2 

엽기(?)적 범주 순서

Far above average     Above average           Average     Below average 
              483              1118               666                21 
Far below average        Don't know         No answer              NA's 
               65               179                 6                 2 

원데이터 범주 순서

    Above average           Average     Below average        Don't know 
              483              1118               666                21 
Far above average Far below average         No answer              NA's 
               65               179                 6                 2 

tidyverse로 깔끔한 범주 순서 정리

Far above average     Above average           Average     Below average 
               65               483              1118               666 
Far below average        Don't know         No answer              NA's 
              179                21                 6                 2 

2.3 요인 범주수준 줄이기

요인 수준/범주 줄이기

요인 수준/범주 줄이기

범주형 변수를 다룰 때 많이 고려해야 하는 것 중에 하나가 요인 범주 수준을 줄이는 것이다. 예를 들어, 결혼 상태에 대한 다양한 경우의 수가 존재한다. 이를 혼인(Married)과 비혼(Not Married)으로 범주를 조정하고자 하는 작업을 살펴보자.

  • 결혼 상태코드: MaritalStatus
    • Divorced, Married, Never married, No answer, Separated, Widowed
  • 혼인여부 상태코드: BaseMarital
    • 혼인(Married), 비혼(Not Married)
     Divorced       Married Never married     No answer     Separated 
          411          1158           675             4            81 
      Widowed          NA's 
          209             2 
Not Married     Married   No answer        NA's 
       1376        1158           4           2 

tidyverse로 깔끔하게 범주를 재지정하면 다음과 같다. 물론 dplyr 팩키지 recode() 함수를 사용한다.

Not Married     Married   No answer        NA's 
       1376        1158           4           2 

2.4 연속형 변수를 범주화

연속형 변수를 범주화

연속형 변수를 범주화

범주가 많은 경우 이를 줄이거나, 숫자를 일정 범위로 묶어 범주화하는 경우가 많다. 숫자형 변수지만 값에 “No answer” 혹은 “89 or order”와 같이 문자가 들어 있는 경우 전체가 문자형이 되고 Base R에서는 요인형 변수로 전환된다.

 [1] "18.000000"   "19.000000"   "20.000000"   "21.000000"   "22.000000"  
 [6] "23.000000"   "24.000000"   "25.000000"   "26.000000"   "27.000000"  
[11] "28.000000"   "29.000000"   "30.000000"   "31.000000"   "32.000000"  
[16] "33.000000"   "34.000000"   "35.000000"   "36.000000"   "37.000000"  
[21] "38.000000"   "39.000000"   "40.000000"   "41.000000"   "42.000000"  
[26] "43.000000"   "44.000000"   "45.000000"   "46.000000"   "47.000000"  
[31] "48.000000"   "49.000000"   "50.000000"   "51.000000"   "52.000000"  
[36] "53.000000"   "54.000000"   "55.000000"   "56.000000"   "57.000000"  
[41] "58.000000"   "59.000000"   "60.000000"   "61.000000"   "62.000000"  
[46] "63.000000"   "64.000000"   "65.000000"   "66.000000"   "67.000000"  
[51] "68.000000"   "69.000000"   "70.000000"   "71.000000"   "72.000000"  
[56] "73.000000"   "74.000000"   "75.000000"   "76.000000"   "77.000000"  
[61] "78.000000"   "79.000000"   "80.000000"   "81.000000"   "82.000000"  
[66] "83.000000"   "84.000000"   "85.000000"   "86.000000"   "87.000000"  
[71] "88.000000"   "89 or older" "No answer"  

Base R에서 먼저 as.numeric으로 자료를 변환시키고 나서 ifelse() 함수를 사용해서 범주화한다.

2.5 파생 요인 변수 생성시킴

변수 조건에 따라 범주를 다시 재그룹화시키거나 숫자를 적당한 범위로 나눌 경우 case_when() 함수를 사용한다. 먼저 적당한 데이터를 생성시킨다. mosaic 팩키지에 HELPmiss 데이터셋이 포함되어 있다. 성별로 음주량에 대한 측정데이터가 담겨있다.

  • sex: 성별
  • i1: 지난 1달동안 하루 평균 섭취 음주량
  • i2: 지난 1달동안 하루 평균 최대 섭취 음주량
  • age: 나이
     sex i1 i2 age
1   male NA 26  37
2   male 56 62  37
3   male  0  0  26
4 female  5  5  39
5   male 10 13  32

3 forcats 팩키지 3

정성적 변수와 더불어 데이터 분석과 모형개발에서 필히 익숙하게 다뤄야 하는 데이터가 범주형 데이터다. 과거 install.packages("tidyverse"), install.packages("forcats") 명령어를 통해서 library(tidyverse), library(forcats)를 통해서 명시적으로 팩키지를 불러와서 적재했으나, 이제는 별도로 설치해서 적재할 필요는 없다. 이유는 현시점 기준 tidyverse의 핵심 일원이 되었기 때문이다.

범주형 데이터를 처리하는 요인(factor) 자료형을 이해하고, tidyverse 생태계에서 범주형 요인을 담당하는 forcats 팩키지를 통해서 범주형 데이터를 자유자재로 다룰 수 있는 역량을 익히자.

4 GSS(General Social Survey) 사례

미국 시카고 대학 독립적인 연구기관 NORC에서 장기적으로 수행하는 설문조사 GSS(General Social Survey) 데이터를 실습자료로 활용한다. tidyverse를 도서관(library)에서 꺼내와서 메모리에 올리게 되면 gss_cat 데이터가 포함되어 있다.

4.1 범주형 변수 시각화

forcats 팩키지에 포함되어 있는 데이터로 원하는 경우 ? gss-cat 명령어를 통해 데이터에 대한 자세한 내용을 확인할 수 있다. gss_cat 데이터프레임에 포함되어 있는 race변수는 is.factor() 함수로 요인형으로 이를 막대그래프로 표현해보자.

ggplot2에서 기본디폴트 설정으로 어떤 값도 없는 수준은 자동으로 drop 시킨다. 만약 변수에 포함된 모든 수준을 보려면 drop=FALSE를 넣어 명시적으로 작성한다.

4.2 단변량 범주형 변수

막대그래프의 경우 fct_infreq() 함수를 사용해서 오름 빈도수로 정렬이 가능하다: fct_rev()와 조합해서 사용해도 좋다. gss_cat 데이터에서 요인 수준을 조정하는데 빈도순으로 오름차순으로 요인을 fct_infreq()로 정렬하고 나서, fct_rev() 함수로 빈도수가 높은 순부터 역으로 요인 수준을 정렬하고 이를 시각화한다.

범주형 변수 결혼상태(marital)는 총 6개 수준이 있는데 빈도수가 다르기 때문에 가장 높은 빈도수부터 낮은 빈도수를 갖는 결혼상태로 정렬한 후에 ggplot()으로 막대그래프를 작성한다.

4.3 범주형 변수 필수기능 두가지 4

요인과 관련되어 가장 많이 사용하는 기능은 다음과 같다.

  • 수준 순서를 바꿔 조정함
  • 수준내부 값을 뭉개거나 변경시키는 변화

수준을 조정하지 않는 경우 전반적인 패턴을 살펴보기 어렵다. 하지만, 수준 순서를 재조장하게 되면 가독성을 훨씬 높일 수 있다. fct_reorder() 함수를 사용하는데 인자가 세개 필요하다.

  • f, 수준을 변경하려는 요인명
  • x, 수준을 변경하는데 사용되는 숫자 벡터에
  • fun, f 값 각각에 대해 다수 값이 있는 경우 이를 요약할 함수, 기본디폴트 함수는 중위수를 산출하는 함수 median.

먼저 mutate()로 요인 수준을 뽑아내서 수준을 재조정한 후에 aes() 함수에서 넣는 것도 가능합니다.

종교별 TV 시청시간에 대한 실제품으로 사용으로 될 수 있도록 마무리를 해보자. 이를 위해서 geom_segment() 함수를 활용하여 직선을 추가한다. 그리고 기타 색상과 점크기를 조정하고 x축과 y축에 대한 라벨과 제목을 추가한다.

4.4 요인변수 내부 명칭 변경

요인변수 내부 수준을 바꾸거나 뭉개서 합치는 기능이 자주 사용되고 있다. 먼저 범주형변수 내부 수준(level)의 명칭을 변경사흔데 fct_recode() 함수를 사용한다. 특히, 시각화를 할 때 범주형변수 수준이 길거나 가독성이 떨어지는 경우 요인변수 수준명칭을 바뀌는 것이 필수적이다.

fct_recode() 함수는 명시적으로 언급되지 않는 수준을 그대로 두고, 존재하지 않는 수준을 우연히 언급할 경우 경고 메시지를 띄운다.

# 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

요인 수준 명칭 변경 사례

# 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

요인 수준 변경시 경고 사례

# 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

4.5 범주형 수준 조정

fct_collapse() 함수는 fct_recode()함수의 변종으로 매우 유용하다. 범주형 데이터를 분석할 경우 범주형 변수의 수준을 합쳐야 하는 경우가 종종 발생된다. 충청북도와 충청남도를 합쳐 충청도로 하거나 충청도의 대전광역시와 세종특별자치시를 충청도에 포함시키는 경우도 이런 유형의 작업에 해당된다.

fct_collapse() 함수에 합쳐질 수준을 정의하면 명시적으로 수준을 보고좋게 정리할 수 있다.

# A tibble: 4 x 2
  partyid     n
  <fct>   <int>
1 other     548
2 rep      5346
3 ind      8409
4 dem      7180

복잡한 데이터셋을 다룰 경우 처리할 변수가 상당히 많은 경우가 있다. 이런 경우 fct_lump() 함수가 유용하다. 범주내 수준에 작은 값이 할당된 경우 이를 합쳐 의미있는 수준에 대한 값이 되도록 만드는 기능을 수행한다. n=10으로 인자값을 넣어주면 해당 변수에 수준이 10개로 지정된다. 상황에 따라서는 fct_lump()함수에 인자로 prob=0.1와 같이 확률이 10% 미만이 되는 것은 모두 기타에 넣는 것도 가능하다. other_level="기타"를 넣어 합쳐지는 수준에 대한 명칭을 별도로 지정하는 것도 가능하다.

# A tibble: 11 x 2
   relig                       n
   <fct>                   <int>
 1 Protestant              10846
 2 Catholic                 5124
 3 None                     3523
 4 Christian                 689
 5 Jewish                    388
 6 기타                      234
 7 Other                     224
 8 Buddhism                  147
 9 Inter-nondenominational   109
10 Moslem/islam              104
11 Orthodox-christian         95

5 캐글 설문 데이터셋

Wrangling categorical data in R에 포함된 GSS 데이터셋이 요인(factor) 자료구조를 다루는데 마치 mtcars, iris, titanic, mnist와 같은 역할을 하고 있다.

원데이터는 파일 크기가 20 메가바이트를 넘고 원본 데이터는 캐글 Kaggle ML and Data Science Survey, 2017 설문조사 학습플랫폼 설문문항로 전형적인 설문문항의 한 사례로 볼 수 있다.

데이터를 가져와서 특정 변수만 추출하고 select(contains("LearningPlatformUsefulness")), 폭넓은 자료형태(wide)를 긴 자료형태(long)로 변형시킨다. 그리고 나서 mutate_if() 함수로 문자형자료형을 요인형자료형으로 변형시키고 나서 데이터를 정제시킨다.

플랫폼별로 빈도수를 조사한 후에 요인형 변수를 수준(level) 조정을 하고 시각화가 깔끔하게 될 수 있도록 라벨명을 조정한다.

작은 창에 각 학습플랫폼별로 내용을 잘 나타날 수 있도록 시각화한다.