이번에는 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)