'요인으로 전환'에 해당되는 글 1건

  1. 2015.07.25 R 연속형 변수를 범주형 변수로 변환하기: cut(), ifesle(), within() (13)

데이터는 크게 (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

 

 

 

 

 

> 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 데이터 프레임이 되겠습니다. 

 

 

 

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

 

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

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. kjh 2016.08.27 16:45  댓글주소  수정/삭제  댓글쓰기

    버전에 따라서 달라진건지는 모르겠지만요,
    within으로 만든 stat_score_5의 클래스가 character라고 나온다고 하셨는데
    코드 복사해서 제가 돌려보면 factor로 나오네요

    그리고 cut함수의 right와 include.lowest 관련해서
    위에 내용중에 코드보면 좀 헷갈리는 부분이 있어서요
    1. 만약 right=FALSE이면 include.lowest옵션은 줄 필요가 없는거 맞나요?
    2. right=FALSE일 경우, 구간 전체의 최대값(include.lowest와 반대되는 값)을 범주로 처리하는 옵션은 없는건가요?(?cut쳐봐도 안보이는 것 같아서요..)

    • kjh 2016.08.27 16:49  댓글주소  수정/삭제

      아 위 질문의 2번항목은 해결됐네요
      include.highest라는 옵션은 없고,
      include.lowest라는 옵션이 right=F일 경우 최대값을 처리해주기도 하는거였네요

  2. kjh 2016.08.27 16:57  댓글주소  수정/삭제  댓글쓰기

    위 2번 질문에 대해서 다른분들 참고하시라고 실습해본 코드 올립니다.

    id <- c("juice", "cola", "cloud", "haha", "light")
    score <- c(0, 10, 11, 20, 30)
    id_score <- data.frame(id, score)

    # right=TRUE이므로 구간을 0<x<=10,10<x<=20,20<x<=30 으로 분할.
    # include.lowest=FALSE 이므로, 0이 포함되지 않아서 juice의 categorized_score값이 NA처리됨.
    attach(id_score)
    id_score <- transform(id_score,
    categorized_score=cut(score, breaks=c(0,10,20,30),
    include.lowest=FALSE, right=TRUE,
    labels=c("C","B","A")
    )
    )
    id_score

    # right=FALSE이므로 구간을 0<=x<10,10<=x<20,20<=x<30 으로 분할.
    # 애초에 구간이 최소값 0도 포함하므로 include.lowest=T를 줄 필요가 없어보임.
    # 그러나 30점인 light의 경우 결과가 NA로 나옴.
    id_score <- transform(id_score,
    categorized_score=cut(score, breaks=c(0,10,20,30),
    right=FALSE,
    labels=c("C","B","A")
    )
    )
    id_score

    # right=FALSE이므로 구간을 0<=x<10,10<=x<20,20<=x<30 으로 분할.
    # 옵션명이 include.lowest이나, right=FALSE인 경우 최대값(여기서는 30)을 처리하는 옵션이 됨.
    # 즉, 아래처럼 include.lowest=T만 추가해주면 30점을 처리해줌.
    id_score <- transform(id_score,
    categorized_score=cut(score, breaks=c(0,10,20,30),
    right=FALSE, include.lowest=TRUE,
    labels=c("C","B","A")
    )
    )
    detach(id_score)
    id_score

  3. kjh 2016.08.27 18:05  댓글주소  수정/삭제  댓글쓰기

    ㅠㅠ 그리고 within이 이해가 안되는 부분이
    stat_score_6 = character(0)
    ~~

    이부분에 대해서, 이게 무슨뜻인지 처음 보는 문법인것 같아서요
    짤막하게나마 설명 부탁드립니다.
    (혹시 블로그 내에 설명되어 있는 부분이 있나요?)

    • R Friend R_Friend 2016.08.27 20:57 신고  댓글주소  수정/삭제

      새로 생성될 변수에 대해서 character 형이라고 미리 설정/할당해주는 것입니다.

      within()함수만의 독특한 프로그래밍 문법이구요, 그냥 이런거구나라고 하고 사용하시면 됩니다.

      사용자정의함수 짤때 가끔 빈 벡터를 미리 설정/할당하고 루프 돌리면서 채워나가는 경우가 있기은 한데요 , 패키지의 함수에서 within() 함수처럼 미리 character 형 미리 설정하는 경우는 매우 드물고 낯선게 사실입니다.

  4. hjh 2016.09.01 19:44  댓글주소  수정/삭제  댓글쓰기

    정리를 잘 해주셔서 정말 감사합니다.

    한 가지 궁금한 점이 있습니다. ifelse나 within을 쓸 때 위의 경우와 달리, stat_score와 새로운 열 stat_score2를 기준으로 하여 a,b,c로 나누고 싶을 땐 어떻게 해야할까요?

    예를 들어, 아래와 같이 데이터를 구성합니다.
    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)
    stat_score2 <- c(1,2,3,4,5,6,7,8,9,10)
    score_d.f2 <- data.frame(student_id, stat_score, stat_score2)

    그리고 ifelse를 쓴다면 기준을 stat_score에 대해선 동일하게 두고,
    stat_score2 에 대한 기준도 만들어서 'a', 'b', 'c'로 범주화할 수 있게 하는 것입니다.
    stat_score2에 대한 기준을 예를 들어 stat_score2> 5와 stat_score2<=5와 같이 나누어서 모든 경우의 수를 다 '&'로 묶어주면 가능할까요?

    실제로 응용해보았는데 계속 에러가 나서 두 개의 열에 있는 데이터 각각의 조건을 합쳐서 새로운 범주형 데이터를 만드는 것은 다른 방법을 써야하는 것인지 여쭤보고 싶습니다!

    • R Friend R_Friend 2016.09.01 19:58 신고  댓글주소  수정/삭제

      hjh님, 가능합니다.

      transform() 함수에 ifelse 를 가자고 두 변수 & 조건을 걸면 됩니다.

      질문하신거에 딱 맞는 예제는 아닙니다만, 아래의 링크 참고하셔서 transform() 함수랑 ifelse & 조건 사용해서 테스트해보시기 바랍니다.

      http://rfriend.tistory.com/57

  5. 산낙지 2016.10.13 17:27  댓글주소  수정/삭제  댓글쓰기

    전에 assign 함수에서 질문했던 것을 transform으로 바꾸어 활용해보았는데요!

    for (i in 1:3) {
    pdt <- transform(pdt,
    paste("birth", i, sep=""), c(1,0,0,0,0)[match(paste("r_birth", i, sep=""), c(1, 2, 3, 4, 5))])
    }

    transform 함수를 loop에 넣을 경우엔 <-나 =를 인식하지 못하더라고요! 그래서 ,로 구분을 했습니다. 그런데 이건 명령어가 인식은 되는데 작동이 안 되네요 ㅠㅠ...
    사실 두 번째에 cut 예제도 loop를 이용하면 간단하게 줄일 수 있는 것 아닌가요? 구글에 아무리 'transform loop in r' 관련해서 검색해도 나오질 않네요 ㅠㅠ transform에는 어떻게 loop를 활용할 수 있는 것인가요?

  6. 배고파 2017.12.20 11:37  댓글주소  수정/삭제  댓글쓰기

    혹시 순서형 요인변수로 왜 만들어야 하는건가요? 시각화 할 때는 순서형으로 만들어 놓으면 그 가나다 순이 아닌 순서형 설정해놓은대로 나오던데 그 이유인지요? 회귀분석 할 때 순서형 변수를 명목형 변수로 바꾸는 경우도 있던데 이건 또 왜이런지 궁굼합니다!

    • R Friend R_Friend 2017.12.20 14:11 신고  댓글주소  수정/삭제

      시각화할때 요인형 변수 level 설정하는 이유는 댓글에 언급해주신 그 이유때문입니다.

      화귀분석할때 범주형변수의 코드별로 1, 0 의 가변수로 만들어 주어면 해당 변수의 코드별 y값에 대한 효과를 반영할 수 있습니다.

    • 배고파 2017.12.24 09:44  댓글주소  수정/삭제

      답변 감사합니다. 그럼 더미변수로 둘 때 순서형요인변수와 명목형변수가 차이가 있나요?