ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Data Science 기초] 데이터 시각화 응용 - 변수의 종류에 따른 시각화
    Data Science/Data Science in R 2018. 10. 23. 16:06

    목표 : 여러가지 데이터를 활용하여 데이터 시각화를 응용하여 보자.


    변수의 종류에 따른 시각화 기법

    변수는 크게 수량형변수(quanlitative variable)와 범주형변수(categorical variable)로 구분된다.


    ㆍ수량형변수 : 국민소득, 평균수명, 키, 몸무게

    범주형변수 : 국가, 성별, 혈액형

    범주형변수 중 학점이나 학년처럼 순서가 있는 경우 순서형(ordinal) 변수라고도 한다.


    이처럼 변수의 종류에 따라 적용할 수 있는 시각화기법이 조금씩 다르다.


    1. 한 수량형 변수 - 하나의 연속 변수의 시각화를 위한 기법


    1) 히스토그램(histogram) : 많은 경우 이것으로 충분

    2) 도수폴리곤(frequancy polygon) : 막대대신 도수를 직선으로 연결

    3) 커널밀도추정함수(kernel density estimation) : 매끄러운 곡선으로 분포를 추정


    * 전체적인 분포의 형태를 보고자 하는 것으로 다음과 같은 사항들을 살펴보면 좋다.


    1. 이상점은 없는가?

    2. 전반적인 분포 모양은? 종모양인가, 한쪽으로 치우쳤는가, 피크가 여러 개인가?

    3. 종모양의 분포를 만들기 위한 변환이 필요한가?

    4. 히스토그램이 너무 자세하거나 거칠지는 않은가? 이런 경우 구간의 폭을 재조절해본다.


    하나의 연속 변수의 시각화를 위해 gapminder 데이터를 시각화 해보겠다. 


    gapminder의 gdpPercap(일인단국민소득) 수량형 변수의 히스토그램, 로그변환한 변수의 히스토그램, 도수폴리곤, 커널밀도추정함수를 그려주는 R명령은 다음과 같다.

    (gapminder data 분석에 대한 자료 링크 : https://kuklife.tistory.com/43?category=797850)


    library(gridExtra)

    > p1 = gapminder %>% ggplot(aes(x=gdpPercap)) + geom_histogram() #히스토그램 함수

    > p2 = gapminder %>% ggplot(aes(x=gdpPercap)) + geom_histogram() + scale_x_log10()

    > p3 = gapminder %>% ggplot(aes(x=gdpPercap)) + geom_freqpoly() + scale_x_log10() #도수폴리곤 함수

    > p4 = gapminder %>% ggplot(aes(x=gdpPercap)) + geom_density() + scale_x_log10() #커널밀도추정함수

    > grid.arrange(p1,p2,p3,p4, nrow=2, ncol = 2)



    기초 통계량은 summary() 함수로 알아낼 수 있다.


     > summary(gapminder) 

            country        continent        year         lifeExp           pop           

     Afghanistan:  12   Africa  :624   Min.   :1952   Min.   :23.60   Min.   :6.001e+04  

     Albania    :  12   Americas:300   1st Qu.:1966   1st Qu.:48.20   1st Qu.:2.794e+06  

     Algeria    :  12   Asia    :396   Median :1980   Median :60.71   Median :7.024e+06  

     Angola     :  12   Europe  :360   Mean   :1980   Mean   :59.47   Mean   :2.960e+07  

     Argentina  :  12   Oceania : 24   3rd Qu.:1993   3rd Qu.:70.85   3rd Qu.:1.959e+07  

     Australia  :  12                  Max.   :2007   Max.   :82.60   Max.   :1.319e+09  

     (Other)    :1632                                                                    

       gdpPercap       

     Min.   :   241.2  

     1st Qu.:  1202.1  

     Median :  3531.8  

     Mean   :  7215.3  

     3rd Qu.:  9325.5  

     Max.   :113523.1 


    2. 한 범주형 변수 - 하나의 범주형 변수를 위한 시각화기법


    1) 막대그래프(bar chart)

    2) 원형그래프(pie chart)


    하나의 범주형 변수의 시각화를 위해 diamonds 데이터를 시각화 해보겠다.


    먼저, diamonds data를 확인해 본 후 하나의 범주형을 택하여 시각화해보자.


    > glimpse(diamonds)   # diamonds 데이터


    Observations: 53,940

    Variables: 10

    $ carat   <dbl> 0.23, 0.21, 0.23, 0.29, 0.31, 0.24, 0.24, 0.26, 0.22, 0.23, 0.30, 0.23, 0.22, 0.31,...

    cut     <ord> Ideal, Premium, Good, Premium, Good, Very Good, Very Good, Very Good, Fair, Very Go...

    $ color   <ord> E, E, E, I, J, J, I, H, E, H, J, J, F, J, E, E, I, J, J, J, I, E, H, J, J, G, I, J,...

    $ clarity <ord> SI2, SI1, VS1, VS2, SI2, VVS2, VVS1, SI1, VS2, VS1, SI1, VS1, SI1, SI2, SI2, I1, SI...

    $ depth   <dbl> 61.5, 59.8, 56.9, 62.4, 63.3, 62.8, 62.3, 61.9, 65.1, 59.4, 64.0, 62.8, 60.4, 62.2,...

    $ table   <dbl> 55, 61, 65, 58, 58, 57, 57, 55, 61, 61, 55, 56, 61, 54, 62, 58, 54, 54, 56, 59, 56,...

    $ price   <int> 326, 326, 327, 334, 335, 336, 336, 337, 337, 338, 339, 340, 342, 344, 345, 345, 348...

    $ x       <dbl> 3.95, 3.89, 4.05, 4.20, 4.34, 3.94, 3.95, 4.07, 3.87, 4.00, 4.25, 3.93, 3.88, 4.35,...

    $ y       <dbl> 3.98, 3.84, 4.07, 4.23, 4.35, 3.96, 3.98, 4.11, 3.78, 4.05, 4.28, 3.90, 3.84, 4.37,...

    $ z       <dbl> 2.43, 2.31, 2.31, 2.63, 2.75, 2.48, 2.47, 2.53, 2.49, 2.39, 2.73, 2.46, 2.33, 2.71,...


     > diamonds %>% ggplot(aes(cut)) + geom_bar()   # cut 변수 막대그래프


     diamonds %>% ggplot(aes(x=factor(1),fill=factor(cut))) + geom_bar() + coord_polar(theta="y")  # cut 변수 원형 그래프


       


    보다시피 원형그래프의 장점은 상대적인 크기 비교가 보다 용이하다는 점이 있다.


    그럼, diamonds 데이터에서 범주형 변수의 수치 요약을 보자


    먼저, 도수 분포표이다.

    * 도수 분포란? 통계학에서 도수 분포(度數分布, frequency distribution)는 표본의 다양한 산출 분포를 보여주는 목록, 표, 그래프이다. 표에 들어가는 각 항목은 특정 그룹이나 주기 안에 값이 발생한 빈도나 횟수를 포함하고 있으며 이러한 방식으로 표는 표본 값의 분포를 요약한다. (출처 : 위키 백과사전)


     > table(diamonds $ cut) #도수분포표

         Fair      Good Very Good   Premium     Ideal 

         1610      4906     12082     13791     21551 


    > prop.table(table(diamonds $ cut)) #상대도수분포표

          Fair       Good  Very Good    Premium      Ideal 

    0.02984798 0.09095291 0.22398962 0.25567297 0.39953652 


    > round(prop.table(table(diamonds$cut))*100, 1) # 반올림하여 %로 표

         Fair      Good Very Good   Premium     Ideal 

          3.0       9.1      22.4      25.6      40.0 


    dplyr 라이브러리를 사용해서 통계량을 계산하는 방법은 다음과 같다.

    * group_by () :기존의 tbl을 취하여 "그룹 단위로"작업이 수행되는 그룹화 된 tbl로 변환

    * tally() : 사용자가 처음으로 집계하는지 또는 재시도하는지에 따라 n() 또는 sum(n)을 호출하는 편리한 요약용 wrapper.(count ()는 비슷하지만 뒤에 group_by ()와 ungroup ()을 호출)

    * mutate() : 열추가 함수

    * round() : x 를 소수점 n자리로 반올림 


    > diamonds %>% group_by(cut) %>% tally() %>% mutate(pct = round(n / sum(n) * 100, 1))

    # A tibble: 5 x 3

      cut           n   pct

      <ord>     <int> <dbl>

    1 Fair       1610   3  

    2 Good       4906   9.1

    3 Very Good 12082  22.4

    4 Premium   13791  25.6

    5 Ideal     21551  40  


    3. 두 수량형 변수


    두 수량형 변수의 시각화는 기본적으로 산점도(scatterplot)을 이용한다.

    한 x, y 좌표에 여러 개의 중복된 관측치가 있을 때는 geom_jitter()를 사용하여 점들을 조금 흩어준다.


    diamonds와 mpg data를 토대로 시각화하여 보자.


    먼저, mpg 데이터는 자동차의 mpg 정보를 담아놓은 data로써 형태는 하단과 같다.


    > glimpse(mpg)

    Observations: 234

    Variables: 13

    $ manufacturer <chr> "audi", "audi", "audi", "audi", "audi", "audi", "audi", "audi", "audi", "audi"...

    $ model        <chr> "a4", "a4", "a4", "a4", "a4", "a4", "a4", "a4 quattro", "a4 quattro", "a4 quat...

    $ displ        <dbl> 1.8, 1.8, 2.0, 2.0, 2.8, 2.8, 3.1, 1.8, 1.8, 2.0, 2.0, 2.8, 2.8, 3.1, 3.1, 2.8...

    $ year         <int> 1999, 1999, 2008, 2008, 1999, 1999, 2008, 1999, 1999, 2008, 2008, 1999, 1999, ...

    $ cyl          <int> 4, 4, 4, 4, 6, 6, 6, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, ...

    $ trans        <chr> "auto(l5)", "manual(m5)", "manual(m6)", "auto(av)", "auto(l5)", "manual(m5)", ...

    $ drv          <chr> "f", "f", "f", "f", "f", "f", "f", "4", "4", "4", "4", "4", "4", "4", "4", "4"...

    $ cty          <int> 18, 21, 20, 21, 16, 18, 18, 18, 16, 20, 19, 15, 17, 17, 15, 15, 17, 16, 14, 11...

    $ hwy          <int> 29, 29, 31, 30, 26, 26, 27, 26, 25, 28, 27, 25, 25, 25, 25, 24, 25, 23, 20, 15...

    $ fl           <chr> "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p"...

    $ class        <chr> "compact", "compact", "compact", "compact", "compact", "compact", "compact", "...

    $ total        <dbl> 23.5, 25.0, 25.5, 25.5, 21.0, 22.0, 22.5, 22.0, 20.5, 24.0, 23.0, 20.0, 21.0, ...

    $ test         <chr> "pass", "pass", "pass", "pass", "pass", "pass", "pass", "pass", "pass", "pass"...


    > p1 = diamonds %>% ggplot(aes(carat, price)) + geom_point()

    > p2 = diamonds %>% ggplot(aes(carat, price)) + geom_point(alpha=.01)

    > p3 = mpg %>% ggplot(aes(cyl, hwy)) + geom_point()

    > p4 = mpg %>% ggplot(aes(cyl, hwy)) + geom_jitter()

    > grid.arrange(p1,p2,p3,p4,nrow=2,ncol=2)

    diamonds의 경우에는 점들의 밀도가 너무 많아 alpha 값을 줄여주었고, carat에 여러 값들이 있어 geom_jitter()를 통해 흩어 놓은 모습을 볼 수 있을 것이다.


    * 산점도 시각화를 살펴 볼 때는 다음과 같은 내용에 주의해야 한다.


    1. 데이터의 개수가 너무 많으면 일부만 표본화하여 시각화

    2. 데이터의 개수가 너무 많으면 점들을 좀 더 투명하게 만들어봄 (alpha 옵션)

    3. 변수에 로그 혹은 제곱근 변환이 필요한지 고려

    4. 데이터의 상관관계 관찰(강한지 혹은 약한지)

    5. 데이터의 관계가 선형인지 비선형인지 관찰

    6. 이상점이 있는 지 관찰

    7. 두 변수 사이에 자연스러운 인과관계가 있는지 고려. 만약 있다면 원인변수를 가로(X)축, 결과변수를 세로(Y)축에 놓는 것이 자연스러움


    두 개 이상의 연속 변수를 다룰 때는 산점도 행렬이 효과적이다


    > pairs(diamonds %>% sample_n(1000)) #pairs()는 base R package의 함수


    4. 수량형 변수와 범주형 변수


    앞서 언급했듯이 두 변수 사이의 인과 관계가 있다면 설명변수를 X로, 반응변수를 Y로 놓는다고 하였다.

    이번엔 X변수가 범주형일 경우에는 병렬 상자그림(side-by-side boxplot)으로 데이터를 시각화한다.


    > mpg %>% ggplot(aes(class, hwy)) + geom_boxplot()   # 각 class에 대하여 hwy(연비)의 상자그림 출력


    위 그림으로부터 hwy 변수의 분포를 각 class 변수의 값 별로 볼 수 있다.


    하지만 기본적인 플롯은 많은 정보를 제공하지만 몇 가지 아쉬운 점이 있다.


    1. 각 class 그룹의 관측치는 얼마나 될까?

    2. class 변수의 순서를 기본적인 순서(알파벳 순서)와 다르게 할 수는 없을까?


    다음 명령은 이러한 시도의 과정을 보여준다.

    ##### 점들을 흩트리고, 투명하게 만듬 #####

    > p1 = mpg %>% ggplot(aes(class, hwy)) + geom_jitter(col='gray') + geom_boxplot(alpha=.5)ㅊ


    ##### reorder()를 이용하여 class변수를 hwy의 중앙값 순으로 재정렬 #####

    > p2 = mpg %>% mutate(class=reorder(class, hwy, median)) %>% ggplot(aes(class, hwy)) + geom_jitter(col='gray') +geom_boxplot(alpha=.5)


    ##### factor()를 이용하여 수동으로 class의 순서를 재정렬 #####

    > p3 =  mpg %>% mutate(class=factor(class, levels=c("2seater", "subcompact", "compact", "midsize","minivan","suv", "pickup"))) %>% ggplot(aes(class, hwy)) + geom_jitter(col='gray') + geom_boxplot(alpha=.5)


    ##### coord_flip()을 이용하여 축 방향 조정 #####

    > p4 = mpg %>% mutate(class=factor(class, levels=c("2seater", "subcompact", "compact", "midsize","minivan","suv", "pickup"))) %>% ggplot(aes(class, hwy)) + geom_jitter(col='gray') + geom_boxplot(alpha=.5) + coord_flip()


    > grid.arrange(p1,p2,p3,p4,nrow=2,ncol=2)


    첫 번째 명령에서 산점도를 위해 geom_point() 레이어 대신 geom_jitter()를 사용한 이유는, x, y좌표에 여러 개의 중복된 관측치가 있을 때 점들이 겹쳐 보이는 것을 피하기 위함이다.

    geom_jitter()레이어를 먼저 그려주고, 그 다음에 geom_boxplot()레이어를 반투명하게 더해 각 집단의 관측치 개수를 나타냈다.


    두 번째 명령에서는 reorder() 명령을 사용하여 class 함수를 각 그룹에서 hwy 변수의 중간값의 오름차순으로 범주를 정해주었다.


    세 번째 명령에서는 factor() 함수의 level= 옵션을 이용해 수동으로 범주의 순서를 결정해 주었다.


    네 번째 명령에서는 coord_flip()함수를 이용하여 class 변수의 그룹명이 y축에 가로로 나타나게 해주었다.


    즉, 시각화 분석을 통해 아래와 같은 정보를 알 수 있다.


    1. 차량의 무게가 무거워지면 연비가 나빠진다.

    2. 하지만, 2seater과 midsize까지의 등급에서는 큰 차이가 없으며, 2seater는 차가 가벼울지 모르나 스포츠카 일 수 있으므로 연비가 평균적으로 더 나쁨을 알 수 있다.

    3. 일부 suubcompact 차량은 연비가 40mpg보다도 높은 고연비 차량임을 알 수 있다.


    * 병렬상자그림을 그릴 때는 다음과 같은 점들을 고려하면 좋다.


    1. 범주형변수의 적절한 순서를 고려

    2. 수량형 변수에 변환이 필요한지?

    3. 수량형변수의 분포 모양이 어떠한지 관찰

    4. 각 그룹내 관측치가 충분한지 관찰

    5. 다양한 옵션 시도하며 차트를 개선해 나가야 함


    5. 두 범주형 변수


    실제로 두 범주형 변수를 다룰 일은 아주 많지 않다.

    도수 분포를 알아내기 위해서는 xtabs() 함수를, 결과를 시각화하기 위해서는 mosaicplot()을 사용한다는 정도만 알면 된다.


    예제로 사용할 데이터는 Titanic data로써, 타이타닉호에서 2201명의 탑승자 중 선실 등급, 성별, 나이 별로 생존자 수를 기록한 데이터이다.

    데이터의 형태는 다음과 같다.


     > glimpse(data.frame(Titanic))# data table 형태를 data frame 형태로 바꾸어 봄

    Observations: 32

    Variables: 5

    $ Class    <fct> 1st, 2nd, 3rd, Crew, 1st, 2nd, 3rd, Crew, 1st, 2nd, 3rd, Crew, 1st, 2nd, 3rd, Crew...

    $ Sex      <fct> Male, Male, Male, Male, Female, Female, Female, Female, Male, Male, Male, Male, Fe...

    $ Age      <fct> Child, Child, Child, Child, Child, Child, Child, Child, Adult, Adult, Adult, Adult...

    $ Survived <fct> No, No, No, No, No, No, No, No, No, No, No, No, No, No, No, No, Yes, Yes, Yes, Yes...

    $ Freq     <dbl> 0, 0, 35, 0, 0, 0, 17, 0, 118, 154, 387, 670, 4, 13, 89, 3, 5, 11, 13, 0, 1, 13, 1...


    이러한 형태의 데이터를 나타내는 또 다른 방법은 다음처럼 고차원 행렬을 이용하는 것이다.


     > xtabs(Freq~Class+Sex+Age+Survived,data.frame(Titanic))

    , , Age = Child, Survived = No


          Sex

    Class  Male Female

      1st     0      0

      2nd     0      0

      3rd    35     17

      Crew    0      0


    , , Age = Adult, Survived = No


          Sex

    Class  Male Female

      1st   118      4

      2nd   154     13

      3rd   387     89

      Crew  670      3


    , , Age = Child, Survived = Yes


          Sex

    Class  Male Female

      1st     5      1

      2nd    11     13

      3rd    13     14

      Crew    0      0


    , , Age = Adult, Survived = Yes


          Sex

    Class  Male Female

      1st    57    140

      2nd    14     80

      3rd    75     76

      Crew  192     20


    데이터가 이처럼 xtabs 고차원 행렬로 정리되면 모자익플롯으로 시각화가 가능하다.


    > par(mfrow=c(1,2)) 

    > mosaicplot(Titanic, main = "Survial on the Titanic")

    > mosaicplot(Titanic, main = "Survial on the Titanic", color = TRUE)


    시각화 분석을 통해 아래와 같은 정보를 알 수 있다.


    1. 탑승 인원은 선원이 가장 많고 그 다음은 3등실~1등실

    2. 아이의 비율이 가장 많은 승객 층은 3등실

    3. 여성의 비율은 선원층이 가장 적고, 1등실에서 가장 많음

    4, 각 선실등급, 성별, 나이의 조합 중에서 생존율이 가장 높은 집단은 밝게 표시된 영역이 가장 많은 그룹임

    5. 1등실 여성은 대부분 생존했으며, 그 다음은 2~3등실 여성

    6. 가장 사망률이 높은 조합은 2등실 남자 어른

    7. 전반적으로 '레이디 퍼스트'인 여성에 대한 배려 때문에 남자들은 1등실 승객도 사망률이 무척 높다.


    그럼, 어른과 아이 중 누가 더 생존율이 높을지 알아보자. 이것을 알아보기 위해서는 이 데이터를 아이-어른 변수 Age와 생존 변수를 남기고 다른 값들은 더하면 된다.

    * apply() : 배열 또는 행렬의 여백에 함수를 적용하여 얻은 벡터 또는 배열 또는 값 목록을 반환


     > apply(Titanic, c(3,4), sum)

           Survived

    Age       No Yes

      Child   52  57

      Adult 1438 654


    > round(prop.table(apply(Titanic, c(3,4), sum), margin=1),3)

           Survived

    Age        No   Yes

      Child 0.477 0.523

      Adult 0.687 0.313


    즉, 아이의 생존율은 52%, 어른은 31%이다. 


    남-녀 생존율의 비교도 해보자.


    > apply(Titanic, c(2,4), sum)

            Survived

    Sex        No Yes

      Male   1364 367

      Female  126 344


    > round(prop.table(apply(Titanic, c(2,4), sum), margin=1),3)

            Survived

    Sex         No   Yes

      Male   0.788 0.212

      Female 0.268 0.732


    즉, 여성의 생존율은 73%, 남성의 생존율은 21%이다.(레이디 퍼스트...)


    위와 같이 계산을 직접 행렬에 가능하지만, dplyr의 gruop_by()를 권장한다.(어떤 변수가 요약되는지 분명히 들어나기 때문)


    > t2=data.frame(Titanic)

    > t2 %>% group_by(Sex) %>% summarize(n=sum(Freq), survivors = sum(ifelse(Survived=="Yes", Freq, 0))) %>% mutate(rate_survival = survivors/n)

    # A tibble: 2 x 4

      Sex        n survivors rate_survival

      <fct>  <dbl>     <dbl>         <dbl>

    1 Male    1731       367         0.212

    2 Female   470       344         0.732


    * 참고문헌 : 실리콘밸리 데이터 과학자가 알려주는 따라하며 배우는 데이터 과학(저자 : 권재명)

    댓글

by KUKLIFE