본 R 예제는 데이터 과학 입문서인 헬로 데이터 과학의 부록으로 제공됩니다. 좀더 자세한 설명은 제 블로그나 페이스북을 참조하시기 바랍니다.

환경 설정하기

이번에는 R로 데이터를 준비하는 과정을 살펴보자. 이 장의 내용은 Hadley Wickham교수의 논문에 일부 기반한다. 1 우선 2장에서 설명한대로 GitHub에서 다운받은 파일이 있는 곳으로 작업 디렉토리를 설정하자. (단축키: Ctrl+Shift+H) 이후 이 책에서 제공하는 R 라이브러리를 로딩하고, 이 장에서 사용할 패키지를 불러오자.

source("dbook.R")
load.packages(c("stringr", "reshape2", "dplyr", "ggplot2"))
## [1] loading stringr
## [1] loading reshape2
## [1] loading dplyr
## [1] loading ggplot2

우선 원본 데이터를 불러와서 살펴보자. 테이블의 각 속성이 서로 다른 소득 구간에 속하는 사람들의 분포를 나타내는 것을 알 수 있다. 이 데이터는 각 행이 개별 속성을 포함해야 한다는 표준 테이블의 원칙을 2 어기고 있다.

# pew.txt을 읽어들이고 확인한다.
pew.raw <- read.delim("pew.txt", check.names=FALSE, stringsAsFactors=FALSE)
head(pew.raw)
##           religion <$10k $10-20k $20-30k $30-40k $40-50k $50-75k $75-100k
## 1         Agnostic    27      34      60      81      76     137      122
## 2          Atheist    12      27      37      52      35      70       73
## 3         Buddhist    27      21      30      34      33      58       62
## 4         Catholic   418     617     732     670     638    1116      949
## 5       Don't know    15      14      15      11      10      35       21
## 6 Evangelical Prot   575     869    1064     982     881    1486      949
##   $100-150k >150k Don't know
## 1       109    84         96
## 2        59    74         76
## 3        39    53         54
## 4       792   633       1489
## 5        17    18        116
## 6       723   414       1529

표준 테이블로 변환하기

앞서 편리한 분석을 위해서는 표준 테이블로 변환해야 한다고 언급했다. 표준 테이블은 각 행에 관찰 항목이, 그리고 각 열에 개별 속성이 들어간 테이블이다. R에서는 이를 위해 melt() 함수를 사용한다.

# religion 속성을 제외한 다른 속성을 측정값(measure)으로 변환한다.
pew.tidy <- melt(pew.raw, "religion")

# 데이터의 속성 이름을 지정한다.
names(pew.tidy) <- c("religion", "income", "count")
head(pew.tidy)
##           religion income count
## 1         Agnostic  <$10k    27
## 2          Atheist  <$10k    12
## 3         Buddhist  <$10k    27
## 4         Catholic  <$10k   418
## 5       Don't know  <$10k    15
## 6 Evangelical Prot  <$10k   575

소득 속성 추가하기

그리고 구간 형태로 되어있는 소득을 구간별 평균과 같은 수치값으로 바꾸는 자료 변환 작업이 필요하다. 이 작업을 위해 우선 주어진 범위를 수치형으로 바꾸는 range.to.number() 함수를 다음과 같이 정의하자.

# 범위 형태의 문자열을 숫자로 바꾸는 함수
range.to.number <- function(v){
    # 정규식을 사용하여 범위 문자열에서 숫자 문자열을 모두 추출한다.
    range.values = str_extract_all(v, "\\d+")
    # 숫자가 추출되면, 숫자 문자열을 수치형으로 바꾸고 그 평균을 구한다.
    if(length(range.values[[1]]) > 0)
        mean(sapply(range.values, as.integer))
    else
        NA
}

range.to.number() 함수는 문자열에서 숫자를 추출하고, 문자열이 발견되지 않는 경우 NA를 반환한다. NA는 R에서 값이 존재하지 않는 경우에 사용되는 기호다.

range.to.number("$10k")
## [1] 10
range.to.number("$10-20k")
## [1] 15
range.to.number("No Number")
## [1] NA

이제 위 함수를 실제 데이터에 적용해보자. 그런데 일부 항목에서 숫자 형태의 소득을 구하는 데 실패하는 것을 알 수 있다. 이는 일부 항목의 소득 구간이 숫자가 아닌 ‘Don’t Know/refused’라는 텍스트 형태이기 때문이다. 이번 분석에서는 해당 항목을 제외하도록 하자. sapply()는 첫번째 인자로 주어지는 속성 벡터에 두번쨰 인자로 주어지는 함수를 적용하는 명령이다.

# 소득 값을 숫자로 변환한다.
pew.tidy$income.usd = sapply(pew.tidy$income, range.to.number) * 1000
head(pew.tidy$income.usd)
## [1] 10000 10000 10000 10000 10000 10000

정리 작업을 완료한 테이블을 살펴보자. 아래 표에서 income.usd는 원본 데이터의 소득 구간에 해당하는 미국 달러 단위의 소득 수준을 나타낸다.

# 변환에 실패한 항목을 버린다.
pew.tidy = na.omit(pew.tidy)
head(pew.tidy)
##           religion income count income.usd
## 1         Agnostic  <$10k    27      10000
## 2          Atheist  <$10k    12      10000
## 3         Buddhist  <$10k    27      10000
## 4         Catholic  <$10k   418      10000
## 5       Don't know  <$10k    15      10000
## 6 Evangelical Prot  <$10k   575      10000

이제 위 데이터를 시각화해보자. 아래 플롯은 정리된 테이블을 바탕으로 각 종교별 교인들의 구간별 소득 분포를 시각적으로 보여준다. 위 테이블에 있는 데이터를 그대로 옮겨 놓으면 아래와 같은 플롯이 완성된다.

qplot(income.usd, religion, data = pew.tidy, size=count)

데이터 집계하기

이제 위 데이터를 집계하는 방법을 생각해보자. 이런 집계가 가능한 것은 데이터를 표준 테이블 형태로 변형했기 때문이다. 우선 쉽게 생각할 수 있는 집계 방법은 종교 집단에 따라 총 신자 수 및 평균 소득을 구하는 것이다.

pew.agg = pew.tidy %>%                                              # 원본 데이터를
    group_by(religion) %>%                                          # religion을 기준으로 그룹화하고
    summarize(total.count = sum(count),                             # 전체 교인의 명수와
              avg.income.usd = mean(income.usd*count) / sum(count)) # 평균 소득을 계산한다
head(pew.agg)
## Source: local data frame [6 x 3]
## 
##           religion total.count avg.income.usd
## 1         Agnostic         730       8218.798
## 2          Atheist         439       8626.297
## 3         Buddhist         357       8056.334
## 4         Catholic        6565       7288.356
## 5       Don't know         156       7421.652
## 6 Evangelical Prot        7943       6263.901

위에서 구한 통계값을 막대 그래프로 나타낸 결과는 다음과 같다. coord_flip() 함수는 세로 바 형태의 그래프를 가로로 회전하는 기능을 한다.

# 종교별 전체 교인의 수 차트
q1 = qplot(x=religion, y=total.count, data=pew.agg, geom="bar", # 차트의 XY축과 데이터 및 형태를 지정한다
           width=0.5, stat="identity") + coord_flip()           # 차트의 세부 모양과 방향을 지정한다

# 종교별 평균 소득 플롯
q2 = qplot(x=religion, y=avg.income.usd, data=pew.agg, geom="bar", 
           width=0.5, stat="identity") + coord_flip()

# 위에서 만든 두개의 플롯을 한 화면에 출력한다. (cols는 열의 개수)
multiplot(q1, q2, cols=2)


  1. Tidy Data http://vita.had.co.nz/papers/tidy-data.pdf

  2. Tidy Data http://vita.had.co.nz/papers/tidy-data.pdf