2장 R 로 데이터 불러오기

이 장에서는

데이터를 처음 받으면 드는 생각은 아마 쉘 도구나 스프레드 시트로 정리를 하려고 먼저 생각할테지만, 실제 분석에 걸리는 시간보다 이런 정제 작업에 걸리는 시간이 훨씬 더 길다는 걸 깨닫는 데에는 별로 긴 시간이 걸리지 않는다. R 의 주요 데이터 타입인 Data Frame 은 구조화된 데이터를 다루는데에 굉장히 적합하며, 많은 일반적인 데이터 포맷을 작업할 수 있는 방법이 R에는 굉장히 많다. 이번 장에서는 파일에 있는 작은 데이터에서부터 시작해서 관계형 데이터 베이스에 적재되어 있는 데이터로 갈 것이다. 이 장을 마칠 때 쯤이면 여러분은 자신감을 갖고 R을 이용해서 분석을 위해 데이터를 추출하고, 변환하고, 불러올 수 있을 것이다.

2.1 파일에서 데이터 불러오기

2.1.1 파일이나 URL을 이용해 잘 구조화된 데이터 작업하기

불러오기 가장 쉬운 데이터 포맷은 행이름이 있는 테이블 구조의 데이터이다. 많은 공공 데이터가 이러한 형태로 되어 있기 때문에 이를 읽어올 수 있다는 것은 우리에게 많은 새로운 기회를 준다.

파일이나 URL 로부터 잘 구조화된 데이터 불러오기

read.table() 함수는 강력하고 유연하다. 다양한 형태의 구분자를 이용할 수 있고, 로컬 파일이나 URL 로부터도 읽어올 수 있다. 파일의 확장자가 .gz 인 경우에는 read.table() 함수가 파일이 gzip 으로 압축된 것으로 생각하고 알아서 압축을 풀고 읽어올 것이다.

uciCar <-read.table(
    "http://win-vector.com/dfiles/car.data.csv",
    sep = ",",
    header = TRUE)

head(uciCar)
##   buying maint doors persons lug_boot safety rating
## 1  vhigh vhigh     2       2    small    low  unacc
## 2  vhigh vhigh     2       2    small    med  unacc
## 3  vhigh vhigh     2       2    small   high  unacc
## 4  vhigh vhigh     2       2      med    low  unacc
## 5  vhigh vhigh     2       2      med    med  unacc
## 6  vhigh vhigh     2       2      med   high  unacc

데이터 살펴보기

  • class(): R object 의 타입을 알려줌
  • help(): 구조나 내부 함수에 대한 문서를 불러줌
  • summary(): 거의 대부분의 R object 에 대한 요약을 알려줌
# uciCar object 의 타입
class(uciCar)
## [1] "data.frame"
# uciCar object 의 요약 통계량
summary(uciCar)
##    buying      maint       doors     persons     lug_boot    safety   
##  high :432   high :432   2    :432   2   :576   big  :576   high:576  
##  low  :432   low  :432   3    :432   4   :576   med  :576   low :576  
##  med  :432   med  :432   4    :432   more:576   small:576   med :576  
##  vhigh:432   vhigh:432   5more:432                                    
##    rating    
##  acc  : 384  
##  good :  69  
##  unacc:1210  
##  vgood:  65
# uciCar object 의 차원 수 
dim(uciCar)
## [1] 1728    7

다른 포맷의 데이터로 작업하기

  • XLS/XLSX: dataframes2xls, WriteXLS, xlsx, XLConnect, readxl
  • JSON: rjson
  • XML: XML
  • MongoDB: rmongodb
  • SQL: DBI

2.1.2 덜 구조화된 데이터 불러오기

데이터가 항상 준비된 형태로 있는 것은 아니다. 컴퓨터에서 사용할 수 있는 형태가 아닌 채로 있는 데이터도 있고, 1장에서 사용한 독일 은행의 신용도 데이터가 그렇다. 테이블 형태의 데이터이긴 하지만 행이름이 없고, 데이터는 암호화되어 있어서 이를 해독하려면 별도의 파일이 필요하다.

d <- read.table(paste("http://archive.ics.uci.edu/ml/",
                "machine-learning-databases/statlog/german/german.data",
                 sep = ""),
           stringsAsFactors = FALSE)

colnames(d) <- c('Status.of.existing.checking.account',
            'Duration.in.month',  'Credit.history', 'Purpose',
            'Credit.amount', 'Savings account/bonds',
            'Present.employment.since',
            'Installment.rate.in.percentage.of.disposable.income',
            'Personal.status.and.sex', 'Other.debtors/guarantors',
            'Present.residence.since', 'Property', 'Age.in.years',
            'Other.installment.plans', 'Housing',
            'Number.of.existing.credits.at.this.bank', 'Job',
            'Number.of.people.being.liable.to.provide.maintenance.for',
            'Telephone', 'foreign.worker', 'Good.Loan')

d$Good.Loan <- as.factor(ifelse(d$Good.Loan==1,'GoodLoan','BadLoan'))

head(d)
##   Status.of.existing.checking.account Duration.in.month Credit.history
## 1                                 A11                 6            A34
## 2                                 A12                48            A32
## 3                                 A14                12            A34
## 4                                 A11                42            A32
## 5                                 A11                24            A33
## 6                                 A14                36            A32
##   Purpose Credit.amount Savings account/bonds Present.employment.since
## 1     A43          1169                   A65                      A75
## 2     A43          5951                   A61                      A73
## 3     A46          2096                   A61                      A74
## 4     A42          7882                   A61                      A74
## 5     A40          4870                   A61                      A73
## 6     A46          9055                   A65                      A73
##   Installment.rate.in.percentage.of.disposable.income
## 1                                                   4
## 2                                                   2
## 3                                                   2
## 4                                                   2
## 5                                                   3
## 6                                                   2
##   Personal.status.and.sex Other.debtors/guarantors Present.residence.since
## 1                     A93                     A101                       4
## 2                     A92                     A101                       2
## 3                     A93                     A101                       3
## 4                     A93                     A103                       4
## 5                     A93                     A101                       4
## 6                     A93                     A101                       4
##   Property Age.in.years Other.installment.plans Housing
## 1     A121           67                    A143    A152
## 2     A121           22                    A143    A152
## 3     A121           49                    A143    A152
## 4     A122           45                    A143    A153
## 5     A124           53                    A143    A153
## 6     A124           35                    A143    A153
##   Number.of.existing.credits.at.this.bank  Job
## 1                                       2 A173
## 2                                       1 A173
## 3                                       1 A172
## 4                                       1 A173
## 5                                       2 A173
## 6                                       1 A172
##   Number.of.people.being.liable.to.provide.maintenance.for Telephone
## 1                                                        1      A192
## 2                                                        1      A191
## 3                                                        2      A191
## 4                                                        2      A191
## 5                                                        2      A191
## 6                                                        2      A192
##   foreign.worker Good.Loan
## 1           A201  GoodLoan
## 2           A201   BadLoan
## 3           A201  GoodLoan
## 4           A201  GoodLoan
## 5           A201   BadLoan
## 6           A201  GoodLoan
# 실행 안 됨
mapping <- list('A11'='... < 0 DM',
                'A12'='0 <= ... < 200 DM',
                'A13'='... >= 200 DM / salary assignments for at least 1 year',
                'A14'='no checking account',
                'A30'='no credits taken/all credits paid back duly',
                'A31'='all credits at this bank paid back duly',
                'A32'='existing credits paid back duly till now',
                'A33'='delay in paying off in the past',
                'A34'='critical account/other credits existing (not at this bank)',
                'A40'='car (new)',
                'A41'='car (used)',
                'A42'='furniture/equipment',
                'A43'='radio/television',
                'A44'='domestic appliances',
                'A45'='repairs',
                'A46'='education',
                'A47'='(vacation - does not exist?)',
                'A48'='retraining',
                'A49'='business',
                'A410'='others',
                'A61'='... < 100 DM',
                'A62'='100 <= ... < 500 DM',
                'A63'='500 <= ... < 1000 DM',
                'A64'='.. >= 1000 DM',
                'A65'='unknown/ no savings account',
                'A71'='unemployed',
                'A72'='... < 1 year',
                'A73'='1 <= ... < 4 years',
                'A74'='4 <= ... < 7 years',
                'A75'='.. >= 7 years',
                'A91'='male : divorced/separated',
                'A92'='female : divorced/separated/married',
                'A93'='male : single',
                'A94'='male : married/widowed',
                'A95'='female : single',
                'A101'='none',
                'A102'='co-applicant',
                'A103'='guarantor',
                'A121'='real estate',
                'A122'='if not A121 : building society savings agreement/life insurance',
                'A123'='if not A121/A122 : car or other, not in attribute 6',
                'A124'='unknown / no property',
                'A141'='bank',
                'A142'='stores',
                'A143'='none',
                'A151'='rent',
                'A152'='own',
                'A153'='for free',
                'A171'='unemployed/ unskilled - non-resident',
                'A172'='unskilled - resident',
                'A173'='skilled employee / official',
                'A174'='management/ self-employed/highly qualified employee/ officer',
                'A191'='none',
                'A192'='yes, registered under the customers name',
                'A201'='yes',
                'A202'='no')
for (i in 1:dim(d)[2]) {
    if (class(d[, i]) == "character") {
        d[, i] <- as.factor(as.character(mapping[d[, i]]))
    }
}

head(d)
##   Status.of.existing.checking.account Duration.in.month
## 1                          ... < 0 DM                 6
## 2                   0 <= ... < 200 DM                48
## 3                 no checking account                12
## 4                          ... < 0 DM                42
## 5                          ... < 0 DM                24
## 6                 no checking account                36
##                                               Credit.history
## 1 critical account/other credits existing (not at this bank)
## 2                   existing credits paid back duly till now
## 3 critical account/other credits existing (not at this bank)
## 4                   existing credits paid back duly till now
## 5                            delay in paying off in the past
## 6                   existing credits paid back duly till now
##               Purpose Credit.amount       Savings account/bonds
## 1    radio/television          1169 unknown/ no savings account
## 2    radio/television          5951                ... < 100 DM
## 3           education          2096                ... < 100 DM
## 4 furniture/equipment          7882                ... < 100 DM
## 5           car (new)          4870                ... < 100 DM
## 6           education          9055 unknown/ no savings account
##   Present.employment.since
## 1            .. >= 7 years
## 2       1 <= ... < 4 years
## 3       4 <= ... < 7 years
## 4       4 <= ... < 7 years
## 5       1 <= ... < 4 years
## 6       1 <= ... < 4 years
##   Installment.rate.in.percentage.of.disposable.income
## 1                                                   4
## 2                                                   2
## 3                                                   2
## 4                                                   2
## 5                                                   3
## 6                                                   2
##               Personal.status.and.sex Other.debtors/guarantors
## 1                       male : single                     none
## 2 female : divorced/separated/married                     none
## 3                       male : single                     none
## 4                       male : single                guarantor
## 5                       male : single                     none
## 6                       male : single                     none
##   Present.residence.since
## 1                       4
## 2                       2
## 3                       3
## 4                       4
## 5                       4
## 6                       4
##                                                          Property
## 1                                                     real estate
## 2                                                     real estate
## 3                                                     real estate
## 4 if not A121 : building society savings agreement/life insurance
## 5                                           unknown / no property
## 6                                           unknown / no property
##   Age.in.years Other.installment.plans  Housing
## 1           67                    none      own
## 2           22                    none      own
## 3           49                    none      own
## 4           45                    none for free
## 5           53                    none for free
## 6           35                    none for free
##   Number.of.existing.credits.at.this.bank                         Job
## 1                                       2 skilled employee / official
## 2                                       1 skilled employee / official
## 3                                       1        unskilled - resident
## 4                                       1 skilled employee / official
## 5                                       2 skilled employee / official
## 6                                       1        unskilled - resident
##   Number.of.people.being.liable.to.provide.maintenance.for
## 1                                                        1
## 2                                                        1
## 3                                                        2
## 4                                                        2
## 5                                                        2
## 6                                                        2
##                                  Telephone foreign.worker Good.Loan
## 1 yes, registered under the customers name            yes  GoodLoan
## 2                                     none            yes   BadLoan
## 3                                     none            yes  GoodLoan
## 4                                     none            yes  GoodLoan
## 5                                     none            yes   BadLoan
## 6 yes, registered under the customers name            yes  GoodLoan

새로운 데이터 살펴보기

https://github.com/WinVector/zmPDSwR/blob/master/Statlog/GCDSteps.R 여기에 가면 한 번에 정리된 게 있으니 이걸 보면서 따라해보자.

table() 함수를 이용하면 범주형 변수에 대한 빈도표를 쉽게 만들 수 있다.

# Purpose 행에 대한 빈도표
summary(d$Purpose)
##            business           car (new)          car (used) 
##                  97                 234                 103 
## domestic appliances           education furniture/equipment 
##                  12                  50                 181 
##              others    radio/television             repairs 
##                  12                 280                  22 
##          retraining 
##                   9
# Purpose 에 따른 Good.Load 의 교차 빈도표
table(d$Purpose, d$Good.Loan)
##                      
##                       BadLoan GoodLoan
##   business                 34       63
##   car (new)                89      145
##   car (used)               17       86
##   domestic appliances       4        8
##   education                22       28
##   furniture/equipment      58      123
##   others                    5        7
##   radio/television         62      218
##   repairs                   8       14
##   retraining                1        8

2.2 관계형 데이터 베이스로 작업하기

많은 운영 환경에서 우리가 원하는 데이터는 파일이 아닌 관계형 또는 SQL 데이터 베이스에 적재되어 있다. 공개해도 되는 데이터의 경우에는 종종 파일로 되어 있지만, 중요한 고객에 대한 데이터 같은 경우에는 데이터 베이스에 저장되어 있기도 한다. 관계형 데이터 베이스의 경우에는 수백만 개의 데이터까지 쉽게 늘릴 수 있고, 병렬화(parallelism), 일관성(consistency), 거래(transaction), 로깅(logging), 감사(audit) 등의 중요한 운영 기능을 제공하기도 한다.

2.2.1 운영 크기의 예제

운영 크기의 예제로 2011년 미국 인구조사 데이터를 사용하려고 한다. 이 데이터는 약 300만 명, 150만 가구에 대한 데이터를 가지고 있다. 각 열은 한 개인 또는 가구에 대한 200개의 값을 가지고 있다.

원본 데이터는 아래 링크에서 받을 수 있다.

United States Population Records: csv_pus.zip(http://www2.census.gov/acs2011_1yr/pums/csv_pus.zip)

United States Housing Unit Records: csv_hus.zip(http://www2.census.gov/acs2011_1yr/pums/csv_hus.zip)

  • 데이터 관리하기

데이터 사이언스에서의 엄격한 규칙은 동일한 결과를 재현해야한다는 것이다. 최소한 여러분의 성공적인 작업을 동일한 단계를 거쳐서 중간의 결과를 사용하지 않고 재현할 수 있어야한다. 모든 단계에서 그 결과를 얻을 수 있는 방법을 적어놓거나, 어디서 온 것인지에 대한 명확한 문서가 있어야한다.

기록하기 데이터 사이언티스트의 역할 중 큰 부분을 차지하는 것이 여러분의 결과를 가지고 설득하고, 이를 재현하는 것이다. 항상 기록하는 것을 강력히 추천한다. 뿐만 아니라, 부록 A에서 이야기한 것처럼 스크립트와 코드를 버전 컨트롤로 관리하는 것도 강력히 추천한다. 지난 주에 발표한 결과를 만들어 낸 코드와 데이터가 정확하게 어디서 기인한 것인지 확실하게 대답해야할 지도 모르기 때문이다.

  • 데이터 베이스에 데이터 적재하기

수백만 줄에 달하는 구조화된 데이터는 데이터 베이스에서 다루는 것이 최선이다. 텍스트 프로세싱 툴에서 작업해도 되지만, 데이터 베이스에서 하는 것이 여러분의 데이터가 열과 행에 맞춰서 잘 준비되었다는 것을 보여주기에는 훨씬 더 나을 것이다.

2.2.2 데이터 베이스에서 R로 데이터 불러오기

데이터 베이스에서 데이터를 불러오기 위해서는 데이터베이스 커넥터가 필요하다. 그러면 R에서 SQL 쿼리를 직접 보낼 수 있다. SQL 은 선언형(declarative) 언어라고 부르는데, SQL 에서는 우리의 데이터 샘플이 어떤 관계를 가지고 있는지를 특정해주지 어떻게 계산을 할 것인지를 다루지는 않기 때문이다. 샘플링한 데이터는 phsample.RData 파일 안에 있고 이는

load(“./PUMS/loadExample/phsample.RData”)

를 이용해서 불러올 수 있다. dhus 는 hus 데이터셋에서 ORIGRANGROUP <=1 인 값만 가지고 온 것인데, ORIGRANGROUP 은 SQL Screwdriver 가 샘플링을 가능하게 하기 위해 추가하는 0-999 사이의 값이다. 이 중에서 0, 1의 값을 가진 row 만 가지고 오는 것이기 때문에 전체 데이터에서 2/1000 을 샘플링 한 것이다. dpus 는 pus 데이터셋에서 phus 데이터셋에 뽑힌 household ID(hus.SERIALNO) 와 동일한 것만 뽑아온 것이다.

샘플링을 너무 자랑스럽게 여기진 말자 많은 데이터 사이언티스트들이 대량의 데이터에 직접 알고리즘을 적용하느라 많은 시간을 낭비하곤 한다. 종종 이는 쓸데 없이 힘을 빼는 것이고, 적당한 사이즈의 데이터 샘플이면 왠만한 모델은 거의 동일한 결과를 얻을 수 있기 때문이다.

2.2.3 PUMS 데이터로 작업하기

데이터 베이스로부터나, 데이터를 R로 불러오는 것은 모델링과 분석에 이용하기 위한 것임을 명심하자. 데이터 분석가는 항상 “데이터를 다뤄야” 하고, 데이터를 불러온 다음엔 빠르게 살펴봐야 한다. 데이터를 다루지 않을 거면 애초에 R 로 불러올 필요도 없을 것이다.

PUMS 데이터 불러오고 다루기

PUMS 에서 각 행은 익명화된 개인이나 가정을 나타낸다. 사람에 대한 데이터는 직업, 교육 정도, 수입, 그리고 다양한 인구통계 값을 가지고 있다.

load(“C:/Dev/pdsr/PUMS/phsample.RData”)

위의 명령어를 이용해서 데이터를 불러오자. 우리가 다룰 예제는 다음의 변수를 가지고 개인의 수입(USD, 컬럼명 PINCP)을 예측하는 것이다.

  • Age - AGEP
  • Employment class - COW
  • Education level - SCHL
  • Sex of worker - SEX

이는 데이터 자체에 집중하기 보다는 모델링 절차를 보여주기 위한 것이다. 결과는 데이터 조건(사용할 데이터의 부분집합) 과 데이터 코딩(데이터를 의미있는 문자로 치환하는 방법)에 크게 달려있다. 이런 이유 때문에 실증적 과학 논문에 “자료와 방법” 섹션이 있고, 여기서 데이터가 어떻게 수집됐고, 준비됐는지 설명하는 것이다. 우리는 다음 조건을 이용해서 “전형적인 상시 근로자” 만 선택할 것이다.

  • 스스로를 상시 근로자라고 표시한 노동자
  • 일주일에 40시간 일한다고 적은 노동자
  • 나이가 20세에서 50세 사이인 노동자
  • 연수입이 $1,000 - $250,000 인 노동자
# Load the sample data
load("C:/Dev/pdsr/PUMS/phsample.RData")

# Select a subset of the Census data considered as a full-time worker
psub <- subset(dpus, with(dpus, (PINCP > 1000) & ESR == 1) &
                   (PINCP <= 250000) & (PERNP > 1000) & (PERNP <= 250000) &
                   (WKHP >= 40) & (AGEP >= 20) & (AGEP <= 50) &
                   (PWGTP1 > 0) & (COW %in% (1:7)) & SCHL %in% (1:24))

데이터 값 바꾸기(Recoding)

이 데이터를 이용해서 작업에 들어가기 전에 가독성을 위해 몇 가지 변수의 데이터를 바꾸려고 한다. 특히, 열거형 정수로 된 변수들을 가독성과 이런 변수들을 일반적인 수치값으로 다루는 것을 방지하기 위해 의미있는 요인-수준으로 바꾸는 것이다.

# Recoding variables
# 성별
psub$SEX <- as.factor(ifelse(psub$SEX == 1, 'M', 'F'))
psub$SEX <- relevel(psub$SEX, 'M')

# 직장의 종류
cowmap <- c("Employee of a private for-profit",
            "Private not-for-profit employee",
            "Local government employee",
            "State government employee",
            "Federal government employee",
            "Self-employed not incorporated",
            "Self-employed incorporated")
psub$COW <- as.factor(cowmap[psub$COW])
psub$COW <- relevel(psub$COW, cowmap[1])

# 학력
schlmap <- c(rep("no high school diploma", 15),
             "Regular high school diploma",
             "GED or alternative credential",
             "some college credit, no degree",
             "some college credit, no degree",
             "Associate's degree",
             "Bachelor's degree",
             "Master's degree",
             "Professional degree",
             "Doctorate degree")
psub$SCHL <- as.factor(schlmap[psub$SCHL])
psub$SCHL <- relevel(psub$SCHL, schlmap[1])

# 데이터셋 나누기
dtrain <- subset(psub, ORIGRANDGROUP >= 500)
dtest  <- subset(psub, ORIGRANDGROUP < 500)