R 범주형 변수로 변환 - r beomjuhyeong byeonsulo byeonhwan

해당 포스트에서는 R에서 범주형 변수 변환을 위한 방법으로 One-Hot Encoding 을 소개합니다.

  • 원문보기 : One-hot encoding in R: three simple methods

0. 배경

데이터 전처리는 모든 분석의 시작 단계로 전처리 수준에 따라 모델의 성능 차이가 크게 발생 할 수 있습니다. 특히 범주형 데이터의 경우, 머신 러닝 알고리즘에 적용하기 위한 전처리(변환)가 필수적이며 아래에서는 그 중 하나의 방법인 One-Hot Encoding을 소개합니다.

R에는 One-Hot Encoding을 지원하는 다수의 패키지들이 있지만, 이번 포스트에서는

제가 분석 업무를 수행하면서 자주 사용하는 3가지 패키지

들을 예시로 설명드리겠습니다.

1. 데이터 준비

먼저 설명에 사용할 샘플 데이터를 생성하겠습니다.

# 샘플 데이터 생성

set.seed(555)
data <- data.frame(
  Outcome = seq(1,100,by=1),
  Variable = sample(c("Red","Green","Blue"), 100, replace = TRUE)
)

head(data)
##   Outcome Variable
## 1       1    Green
## 2       2     Blue
## 3       3     Blue
## 4       4    Green
## 5       5    Green
## 6       6      Red

대상 변수인 Variableas.factor()를 적용해 범주형 데이터로 변환하겠습니다.

# 범주형 데이터 변환

data$Variable <- as.factor(data$Variable)

str(data)
## 'data.frame':    100 obs. of  2 variables:
##  $ Outcome : num  1 2 3 4 5 6 7 8 9 10 ...
##  $ Variable: Factor w/ 3 levels "Blue","Green",..: 1 1 2 1 2 2 2 1 2 2 ...## 

2. One-Hot Encoding 예제

위에서 생성된 데이터를 이용하여 3개의 패키지 및 함수를 이용한 예제를 설명드리겠습니다. 사용법과 변환 결과를 확인하시어 자신에게 맞는 스타일을 이용하시면 됩니다.

Method 1: one_hot in mltools package

첫번째로 설명드릴 함수는 mltools 패키지의 one_hot() 함수입니다. one_hot() 함수는 데이터 내 범주형 데이터를 찾고 해당하는 변수를 새로운 컬럼으로 추가하여 출력해 줍니다. 참고로 해당 함수는 입력 데이터가 data.table 구조를 따라야 하기에 data.table::as.data.table() 함수를 이용하여 변환된 데이터로 적용하였습니다.

출력 결과를 보시면 기존 범주형 변수인 Variable이 사라지고 기존 변수명 Variable 뒤에 _범주명 이 붙은 새로운 변수들이 생기고 데이터는 0 또는 1로 채워진 것을 볼 수 있습니다.

library(mltools)
library(data.table)

newdata <- one_hot(as.data.table(data))

head(newdata)
##    Outcome Variable_Blue Variable_Green Variable_Red
## 1:       1             1              0            0
## 2:       2             1              0            0
## 3:       3             0              1            0
## 4:       4             1              0            0
## 5:       5             0              1            0
## 6:       6             0              1            0 

Method 2: dummyVars in caret package

두번째로 설명드릴 함수는 caret 패키지의 dummyVars() 함수입니다. caret패키지는 머신러닝에 자주 사용되는 패키지로 해당 패키지에서 제공하는 dummyVars() 함수는 개인적으로 활용도가 가장 높습니다.

출력 결과를 보시면 기존 범주형 변수인 Variable이 사라지고 기존 변수명 Variable 뒤에 .범주명 이 붙은 새로운 변수들이 생기고 데이터는 0 또는 1로 채워진 것을 볼 수 있습니다.

library(caret)

dummy <- dummyVars(" ~ .", data=data)
newdata <- data.frame(predict(dummy, newdata = data)) 

head(newdata)
##   Outcome Variable.Blue Variable.Green Variable.Red
## 1       1             1              0            0
## 2       2             1              0            0
## 3       3             0              1            0
## 4       4             1              0            0
## 5       5             0              1            0
## 6       6             0              1            0

Method 3: dcast in reshape2 package

세번째로 설명드릴 함수는 reshape2 패키지의 dcast() 함수입니다. reshape2패키지는 데이터 정제 및 변환에 유용한 패키키이며 해당 패키지에서 제공하는 dcast() 함수는 One-Hot Encoding 외에도 다양하게 활용되니 추가적인 공부를 해보시는걸 추천드립니다.

출력 결과를 보시면 기존 범주형 변수인 Variable이 사라지고 기존 범주명들이 새로운 변수로 생기고 데이터는 0 또는 1로 채워진 것을 볼 수 있습니다.

library(reshape2)

newdata <- dcast(data = data, Outcome ~ Variable, length)

head(newdata)
##   Outcome Blue Green Red
## 1       1    1     0   0
## 2       2    1     0   0
## 3       3    0     1   0
## 4       4    1     0   0
## 5       5    0     1   0
## 6       6    0     1   0

3. 정리

지금까지 범주형 데이터를 One-Hot 형태로 변환하는 패키지 및 함수들을 소개드렸습니다. 개인적으로는 caret 패키지를 자주 이용하고 있어 해당 패키지에서 제공하는 dummyVars() 함수를 주로 사용하고 있습니다. 각 함수의 사용법 및 출력 결과를 보시고 자신에게 맞는 함수를 선택하시면 될 것 같습니다.

추가로 위에서 소개드린 패키지 및 함수들은 One-Hot Encoding 외에도 활용도가 높은 패키지들이니, 해당 기능 외에 다른 기능들도 공부해보시면 좋을 것 같습니다.

4. 관련 링크

[1] One-hot encoding in R: three simple methods

R 범주형 변수로 변환 - r beomjuhyeong byeonsulo byeonhwan

데이터는 크게 (1) 명목형 또는 순서형의 범주형 데이터 (categorical data)와 (2) 연속형 데이터 (continuous data) 로 구분할 수 있습니다.  R에서는 범주형 데이터를 요인(factor)형 데이터 구조라고 부르고 있으며, 순서(order)가 있는 경우는 순서형 요인(ordered factor)라고 해서 구분하기도 합니다.

분석하고자 하는 데이터 셋을 받으면 제일 먼저 데이터 구조와 데이터 형태를 탐색하게 됩니다.  그리고 분석 목적과 시나리오에 따라서 변수를 변환하게 되지요.  이번 포스팅에서는 연속형 변수를 범주형 변수로 변환하는 3가지 방법에 대해서 알아보도록 하겠습니다.  통계기법 중 도수분포표, 교차분할표, 카이제곱 검정이라든지, 로지스틱회귀분석, 그래프 중 막대그림, 원그림, 점그림 등의 경우 범주형 변수로 변환을 해야만 하며, 데이터 탐색 시에도 범주형 변수로 변환하여 분포 형태나 집단 간 비교를 하게 되므로 이번 포스팅은 활용도가 매우 높다고 하겠습니다.

cut() 함수, ifelse() 함수, within() 함수를 이용해서 아래 예를 들어 설명하도록 하겠습니다.

 연속형 변수를 범주형 변수로 변환하기: cut(), ifesle(), within()

(1) cut()

> ## 통계시험 점수 (stat_score) > student_id <- c("s01", "s02", "s03", "s04", "s05", "s06", "s07", "s08", "s09", "s10") > stat_score <- c(56, 94, 82, 70, 64, 82, 78, 80, 76, 78) > mean(stat_score) [1] 76 > hist(stat_score)

> # 데이터 프레임 생성
> score_d.f <- data.frame(student_id, stat_score)
> score_d.f
   student_id stat_score
1         s01         56
2         s02         94
3         s03         82
4         s04         70
5         s05         64
6         s06         82
7         s07         78
8         s08         80
9         s09         76
10        s10         78
 
> rm(student_id, stat_score)

위의 통계시험 성적을 가지고 cut() 함수를 이용하여 "수", "우", "미", "양", "가" 등급을 매겨보도록 하겠습니다.

right = TRUE 옵션을 주면 a < x <= b  와 같이 오른쪽 숫자까지 포함하여 해당 등급을 부여하게 됩니다.

right = FALSE 옵션을 주면 a<= x <b 의 조건으로 등급을 부여하며, include.lowest = TRUE 옵션을 주면 구성요소 값이 최소값과 같아도 변환을 시키게 됩니다.

> ## (1) cut()
> score_d.f <- transform(score_d.f, 
+                  stat_score_1 = cut(stat_score, breaks = c(0, 60, 70, 80, 90, 100), 
+                                     include.lowest = TRUE, 
+                                     right = FALSE, 
+                                     labels = c("가", "양", "미", "우", "수")
+                                     ), 
+                  stat_score_2 = cut(stat_score, breaks = c(0, 60, 70, 80, 90, 100), 
+                                     include.lowest = FALSE, 
+                                     right = FALSE, 
+                                     labels = c("가", "양", "미", "우", "수")
+                                     ),
+                  stat_score_3 = cut(stat_score, breaks = c(0, 60, 70, 80, 90, 100), 
+                                     include.lowest = FALSE, 
+                                     right = TRUE, 
+                                     labels = c("가", "양", "미", "우", "수")
+                                     ), 
+                  stat_score_4 = cut(stat_score, breaks = c(0, 60, 70, 80, 90, 100), 
+                                     include.lowest = TRUE, 
+                                     right = TRUE, 
+                                     labels = c("가", "양", "미", "우", "수")
+                                     )
+                        )
> 
> score_d.f
   student_id stat_score stat_score_1 stat_score_2 stat_score_3 stat_score_4
1         s01         56           가           가           가           가
2         s02         94           수           수           수           수
3         s03         82           우           우           우           우
4         s04         70           미           미           양           양
5         s05         64           양           양           양           양
6         s06         82           우           우           우           우
7         s07         78           미           미           미           미
8         s08         80           우           우           미           미
9         s09         76           미           미           미           미
10        s10         78           미           미           미           미

그런데 사용하다 보면 right 옵션, include.right 옵션, 그리고 labels 부여하는 순서도 그렇고, 머리속이 복잡해집니다. 아래의 ifelse()나 within() 함수는 위의 cut()보다는 수식의 부호를 직접 입력한다는 측면에서 사용하기에 더 편하고 직관적인 면이 있습니다.

(2) ifelse()

> attach(score_d.f)

> score_d.f <- transform(score_d.f, + stat_score_5 = ifelse(stat_score < 60, "가", + ifelse(stat_score >= 60 & stat_score < 70, "양", + ifelse(stat_score >= 70 & stat_score < 80, "미", + ifelse(stat_score >= 80 & stat_score < 90, "우", "수" + )))) + ) > detach(score_d.f) > score_d.f

R 범주형 변수로 변환 - r beomjuhyeong byeonsulo byeonhwan

> class(score_d.f$stat_score_5)
[1] "character"
 

위 표의 제일 오른쪽에 'stat_score_5' 변수가 ifelse() 함수를 이용해서 만든 범주형 변수가 되겠습니다.  cut() 대비 수식 등호, 부등호를 직접 입력하니 직관적으로 분석가가 원하는 범주로 수식을 적을 수 있는 장점이 있습니다만, 범주의 수준(level)이 많아질 수록 괄호 열고 닫는데 유의해야 합니다.  위의 예제의 경우 5개 범주로 나누는데 괄호 열고 "(((("  닫는 것이 "))))" 총 4개가 사용이 되었네요.  갯수 조심하지 않으면 콘솔 창에 에러날거예요.  RStudio 사용하면 ifelse() 괄호 하나씩 더해갈 때 마다 괄호 닫는것도 저절로 생기니 차근 차근 하시면 될겁니다.

그리고 stat_score_5 의 속성(class)이 요인(factor)이 아닌 문자(character)로 되어 있습니다.  만약 요인별로 통계 분석을 하고자 한다면 as.factor() 함수로 문자형을 요인형으로 먼저 변환을 시킨 후에 분석을 진행해야 합니다.

(3) within()

> ## within()
> score_d.f <- within( score_d.f, {
+   stat_score_6 = character(0) 
+   stat_score_6[ stat_score < 60 ] = "가" 
+   stat_score_6[ stat_score >=60 & stat_score < 70 ] = "양" 
+   stat_score_6[ stat_score >=70 & stat_score < 80 ] = "미" 
+   stat_score_6[ stat_score >=80 & stat_score < 90 ] = "우" 
+   stat_score_6[ stat_score >=90 ] = "수" 
+   
+   stat_score_6 = factor(stat_score_6, level = c("수", "우", "미", "양", "가"))
+ })
> 
> score_d.f$stat_score_6
 [1] 가 수 우 미 양 우 미 우 미 미
Levels: 수 우 미 양 가

within() 함수는 먼저 새로 만들 변수 stat_score_6 = character(0)  이라고 해서 문자형 변수라고 신규생성/지정을 해주고 시작합니다.

수식 등호, 부등호로 구간 설정하구요, 제일 마지막 줄에 factor() 함수로 해서 level = c("수", "우", "미", "양", "가") 라고 해서 수준을 지정해 줄 수 있습니다.  성적은 순서(order)가 있으므로 level 에 지정한 순서가 stat_score_6 요인 변수의 level 순서가 되겠습니다.

score_d.f$stat_score_6  라고 해서 indexing을 해서 보면 제일 아랫줄에 "Levels: 수 우 미 양 가" 라고 해서 순서가 제대로 인식되어 있음을 알 수 있습니다.  개인적으로 within() 함수를 순서형 요인변수 만들 때 위 셋 중에서 가장 많이 사용하는 편입니다.

아래는 제일 오른쪽에 within()함수로 만든 stat_score_6 변수까지 모두 한꺼번에 열어본 score_d.f 데이터 프레임이 되겠습니다. 

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

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^