library(tidyverse)
## -- Attaching packages ---------------------------------- tidyverse 1.2.1 --
## √ ggplot2 3.1.0 √ purrr 0.2.5
## √ tibble 1.4.2 √ dplyr 0.7.8
## √ tidyr 0.8.2 √ stringr 1.3.1
## √ readr 1.1.1 √ forcats 0.3.0
## -- Conflicts ------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(haven)
library(readxl)
누구나 한번쯤은 이런말을 많이 들어보았을 것입니다. 데이터 분석을 할때 데이터 전처리에 80%이상 시간을 소요하고 분석은 20%정도의 시간을 소요하게 된다. 프로젝트를 경험해본 사람이라면 이 말에 동의를 할 것입니다. 지금까지 데이터 관리 라는 목적으로 공부를 했습니다. 숲을 공부했다면 지금 부터는 나무를 공부할 것입니다. 변수를 어떻게 관리하고 바꾸고 이런 일련의 과정들을 해볼것입니다. 가장 중요한 부분입니다. 타이디버스 라이브러리 접근에서 데이터 내의 변수를 변환하거나 데이터 내에 새로운 변수를 추가하려면 ’mutate()’함수를 사용합니다. 두 파트로 구성이 되어 있는데 첫번째는 변수에서 결측값을 확인하고 처리하는 방법을 다룰 것입니다. 두번째는 분석자가 원하는 방식으로 변수를 리코딩하는 것입니다.
먼저 결측치를 확인하고 처리하는 방법에 대해 알아보겠습니다.
앞에서도 언급한적이 있습니다. 변수값 중 결측값을 판정하는 함수는 is.na()함수 입니다. 변수의 결측값이 있다면 T를 없다면 F를 반환합니다. 우선 mutate()함수를 이용해 변수값이 실측값인지 결측값인지를 확인할 수 있는 새로운 파생변수를 만든 후에 빈도표를 만들겠습니다.
small_gss<-read_dta("data_gss_panel06.dta") %>% select(starts_with("affrmact_"))
small_gss %>% print(n=3)
## # A tibble: 2,000 x 3
## affrmact_1 affrmact_2 affrmact_3
## <dbl+lbl> <dbl+lbl> <dbl+lbl>
## 1 NA NA NA
## 2 " 1" " 3" " 4"
## 3 NA NA NA
## # ... with 1,997 more rows
결측값이 상당히 많이 있습니다. 우선 affrmact_1변수에서 결측값 여부를 나타내는 새로운 이름의 affrmact_NA_1변수를 만든후에 빈도표를 만들겠습니다.
small_gss2=small_gss %>% mutate(affrmact_NA_1=is.na(affrmact_1)) %>% print(n=10)
## Warning: package 'bindrcpp' was built under R version 3.4.4
## # A tibble: 2,000 x 4
## affrmact_1 affrmact_2 affrmact_3 affrmact_NA_1
## <dbl+lbl> <dbl+lbl> <dbl+lbl> <lgl>
## 1 NA NA NA TRUE
## 2 " 1" " 3" " 4" FALSE
## 3 NA NA NA TRUE
## 4 " 1" " 3" " 4" FALSE
## 5 NA NA NA TRUE
## 6 " 3" " 3" " 1" FALSE
## 7 " 4" NA " 3" FALSE
## 8 " 3" NA NA FALSE
## 9 " 3" " 3" " 3" FALSE
## 10 NA NA NA TRUE
## # ... with 1,990 more rows
small_gss2 %>% count(affrmact_NA_1)
## # A tibble: 2 x 2
## affrmact_NA_1 n
## <lgl> <int>
## 1 FALSE 1251
## 2 TRUE 749
이번에는 세 변수들(affrmact_)에서 발견된 결측값의 총 합을 구해보겠습니다.
small_gss2=small_gss %>% mutate(n.NA=is.na(affrmact_1)+is.na(affrmact_2)+is.na(affrmact_3))
small_gss2 %>% count(n.NA)
## # A tibble: 4 x 2
## n.NA n
## <int> <int>
## 1 0 752
## 2 1 214
## 3 2 330
## 4 3 704
만약 변수가 10개라면 간단하게 rowSums()함수를 이용하면 됩니다. 참고로 is.na(.)여기서 .의 경우 앞의 데이터를 그대로 사용한다는 뜻입니다.
small_gss2=small_gss %>% mutate(t.NA=rowSums(is.na(.))) %>% print(n=10)
## # A tibble: 2,000 x 4
## affrmact_1 affrmact_2 affrmact_3 t.NA
## <dbl+lbl> <dbl+lbl> <dbl+lbl> <dbl>
## 1 NA NA NA 3
## 2 " 1" " 3" " 4" 0
## 3 NA NA NA 3
## 4 " 1" " 3" " 4" 0
## 5 NA NA NA 3
## 6 " 3" " 3" " 1" 0
## 7 " 4" NA " 3" 1
## 8 " 3" NA NA 2
## 9 " 3" " 3" " 3" 0
## 10 NA NA NA 3
## # ... with 1,990 more rows
small_gss2 %>% count(t.NA) #위의 결과와 같음을 볼 수 있습니다.
## # A tibble: 4 x 2
## t.NA n
## <dbl> <int>
## 1 0 752
## 2 1 214
## 3 2 330
## 4 3 704
지금 본 small_gss데이터의 경우 결측값에 대해 NA가 써있습니다. 만약 실측값을 결측값으로 변환해야하는 경우가 생깁니다. 그럴 경우 어떻게 코딩을 할지 생각해봅시다. “data_TESS3_131.sav” 데이터에서 Q2변수를 보겠습니다.
data_131=read_spss("data_TESS3_131.sav")
data_131 %>% count(Q2)
## # A tibble: 8 x 2
## Q2 n
## <dbl+lbl> <int>
## 1 -1 5
## 2 " 1" 143
## 3 " 2" 71
## 4 " 3" 82
## 5 " 4" 107
## 6 " 5" 80
## 7 " 6" 40
## 8 " 7" 65
print_labels(data_131$Q2)
##
## Labels:
## value label
## -1 Refused
## 1 Strongly oppose
## 2 Moderately oppose
## 3 Somewhat oppose
## 4 Neither oppose nor support
## 5 Somewhat support
## 6 Moderately support
## 7 Strongly support
-1의 경우 응답을 거절했습니다. 이럴 경우에 결측값으로 바꿔서 분석을하는 것이 용이합니다. ifelse()함수문을 이용해 바꾸어 보겠습니다.
data_131 %>% mutate(Q2r=ifelse(Q2==-1,NA,Q2)) %>% count(Q2r)
## # A tibble: 8 x 2
## Q2r n
## <dbl> <int>
## 1 1 143
## 2 2 71
## 3 3 82
## 4 4 107
## 5 5 80
## 6 6 40
## 7 7 65
## 8 NA 5
만약에 -1 혹은 4를 결측값으로 지정하고 싶다면 다음과 같이 표현하면 됩니다.
data_131 %>% mutate(Q2r=ifelse(Q2==-1|Q2==4,NA,Q2)) %>% count(Q2r)
## # A tibble: 7 x 2
## Q2r n
## <dbl> <int>
## 1 1 143
## 2 2 71
## 3 3 82
## 4 5 80
## 5 6 40
## 6 7 65
## 7 NA 112
이번엔 숫자를 결측치로 바꾸는 것이 아닌 ’-’로 입력된 것을 결측치로 바꾸는 작업을 해보겠습니다.
seoul_library=read_xls("data_library.xls")
seoul_library %>% print(n=10)
## # A tibble: 182 x 7
## 기간 자치구 계 국립도서관 공공도서관 대학도서관 전문도서관
## <chr> <chr> <dbl> <chr> <dbl> <chr> <chr>
## 1 2010 합계 464 3 101 85 275
## 2 2010 종로구 50 - 4 9 37
## 3 2010 중구 57 - 2 2 53
## 4 2010 용산구 18 - 3 2 13
## 5 2010 성동구 6 - 4 2 -
## 6 2010 광진구 9 - 3 3 3
## 7 2010 동대문구 17 - 2 5 10
## 8 2010 중랑구 4 - 2 1 1
## 9 2010 성북구 16 - 3 8 5
## 10 2010 강북구 10 - 5 3 2
## # ... with 172 more rows
다음과 같이 ’-’표시는 결측치로 인식을 못합니다. 따라서 인식을 시켜줘야합니다.
seoul_library2=seoul_library %>% mutate(계=ifelse(계=='-',NA,계),
국립도서관=ifelse(국립도서관=='-',NA,국립도서관),
공공도서관=ifelse(공공도서관=='-',NA,공공도서관),
대학도서관=ifelse(대학도서관=='-',NA,대학도서관),
전문도서관=ifelse(전문도서관=='-',NA,전문도서관)) %>% print(n=10)
## # A tibble: 182 x 7
## 기간 자치구 계 국립도서관 공공도서관 대학도서관 전문도서관
## <chr> <chr> <dbl> <chr> <dbl> <chr> <chr>
## 1 2010 합계 464 3 101 85 275
## 2 2010 종로구 50 <NA> 4 9 37
## 3 2010 중구 57 <NA> 2 2 53
## 4 2010 용산구 18 <NA> 3 2 13
## 5 2010 성동구 6 <NA> 4 2 <NA>
## 6 2010 광진구 9 <NA> 3 3 3
## 7 2010 동대문구 17 <NA> 2 5 10
## 8 2010 중랑구 4 <NA> 2 1 1
## 9 2010 성북구 16 <NA> 3 8 5
## 10 2010 강북구 10 <NA> 5 3 2
## # ... with 172 more rows
#이번엔 제대로 결측치가 나온 모습을 볼 수 있습니다.
colSums(is.na(seoul_library2))
## 기간 자치구 계 국립도서관 공공도서관 대학도서관
## 0 0 0 161 0 7
## 전문도서관
## 14
만약에 변수가 10개가 있다면 이와 같은 반복작업을 해야할까요?? 여러변수들에 대해 반복되는 작업을 해야할 경우 mutate_all()함수를 사용하면 편리합니다.
seoul_library %>% mutate_all(funs(ifelse(.=='-',NA,.))) %>% print(n=10)
## # A tibble: 182 x 7
## 기간 자치구 계 국립도서관 공공도서관 대학도서관 전문도서관
## <chr> <chr> <dbl> <chr> <dbl> <chr> <chr>
## 1 2010 합계 464 3 101 85 275
## 2 2010 종로구 50 <NA> 4 9 37
## 3 2010 중구 57 <NA> 2 2 53
## 4 2010 용산구 18 <NA> 3 2 13
## 5 2010 성동구 6 <NA> 4 2 <NA>
## 6 2010 광진구 9 <NA> 3 3 3
## 7 2010 동대문구 17 <NA> 2 5 10
## 8 2010 중랑구 4 <NA> 2 1 1
## 9 2010 성북구 16 <NA> 3 8 5
## 10 2010 강북구 10 <NA> 5 3 2
## # ... with 172 more rows
만약 데이터에서 원하는 변수만 선정하고 싶다면 mutate_at()함수를 이용하면 됩니다.
seoul_library %>% mutate_at(3:7,funs(ifelse(.=='-',NA,.))) %>% print(n=10)
## # A tibble: 182 x 7
## 기간 자치구 계 국립도서관 공공도서관 대학도서관 전문도서관
## <chr> <chr> <dbl> <chr> <dbl> <chr> <chr>
## 1 2010 합계 464 3 101 85 275
## 2 2010 종로구 50 <NA> 4 9 37
## 3 2010 중구 57 <NA> 2 2 53
## 4 2010 용산구 18 <NA> 3 2 13
## 5 2010 성동구 6 <NA> 4 2 <NA>
## 6 2010 광진구 9 <NA> 3 3 3
## 7 2010 동대문구 17 <NA> 2 5 10
## 8 2010 중랑구 4 <NA> 2 1 1
## 9 2010 성북구 16 <NA> 3 8 5
## 10 2010 강북구 10 <NA> 5 3 2
## # ... with 172 more rows
또한 mutate_at()함수 내부에는 변수의 이름들을 명시할 수 있습니다. 다음과 같이 vars()함수 내부에 나열하면 됩니다.
seoul_library %>% mutate_at(vars(계,ends_with("도서관")),funs(ifelse(.=="-",NA,.))) %>% print(n=3)
## # A tibble: 182 x 7
## 기간 자치구 계 국립도서관 공공도서관 대학도서관 전문도서관
## <chr> <chr> <dbl> <chr> <dbl> <chr> <chr>
## 1 2010 합계 464 3 101 85 275
## 2 2010 종로구 50 <NA> 4 9 37
## 3 2010 중구 57 <NA> 2 2 53
## # ... with 179 more rows
다음 mutate_if()함수는 변수의 성격이 특정한 조건에 부합할 경우 주어진 함수에 맞게 변수를 변환하는 함수입니다. 위의 ’계’변수와 ’도서관’변수는 다
seoul_library %>% mutate(국립도서관=ifelse(국립도서관=="-",NA,국립도서관),
국립도서관=as.integer(국립도서관)) %>% print(n=3)
## # A tibble: 182 x 7
## 기간 자치구 계 국립도서관 공공도서관 대학도서관 전문도서관
## <chr> <chr> <dbl> <int> <dbl> <chr> <chr>
## 1 2010 합계 464 3 101 85 275
## 2 2010 종로구 50 NA 4 9 37
## 3 2010 중구 57 NA 2 2 53
## # ... with 179 more rows
위의 방식대로 10개의 변수를 한다면 어떨까요? 마찬가지로 mutate_at변수를 이용해 간소화 시켜보겠습니다.
seoul_library %>% mutate_at(vars(3:7),funs(as.integer(ifelse(.=="-",NA,.)))) %>% print(n=3)
## # A tibble: 182 x 7
## 기간 자치구 계 국립도서관 공공도서관 대학도서관 전문도서관
## <chr> <chr> <int> <int> <int> <int> <int>
## 1 2010 합계 464 3 101 85 275
## 2 2010 종로구 50 NA 4 9 37
## 3 2010 중구 57 NA 2 2 53
## # ... with 179 more rows
지금까지 배운것을 종합해서 적용시켜보겠습니다. 1. “data_library.xls”데이터를 불러온 후, 자치구 변수의 값이 ‘합계’인 경우는 제외 2.’-’기호가 들어가면 결측치 !! 3. 도서관 종류에 해당되는 변수는 모두 수치형으로 전환(double로 전환) 4. 연도별 25개 서울시 구의 모든 도서관의 총계 변화를 나타내는 통계치를 나타내기
read_xls("data_library.xls") %>% filter(자치구!="합계") %>%
mutate_at(vars(3:7),funs(as.double(ifelse(.=="-",NA,.)))) %>%
group_by(기간) %>% summarize_if(is.double,funs(sum(.,na.rm=T)))
## # A tibble: 7 x 6
## 기간 계 국립도서관 공공도서관 대학도서관 전문도서관
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2010 464 3 101 85 275
## 2 2011 472 3 109 85 275
## 3 2012 479 3 116 85 275
## 4 2013 489 3 123 88 275
## 5 2014 491 3 132 88 268
## 6 2015 500 3 146 89 262
## 7 2016 502 3 147 88 264
“data_population.xls”를 열면 특정 연령대의 통계수치가 문자형
data=read_xls("data_population.xls")
data %>% mutate_at(vars(22:25),funs(as.double(ifelse(.=="-",NA,.))))
## # A tibble: 312 x 25
## 기간 구분 구분__1 계 `0~4세` `5~9세` `10~14세` `15~19세` `20~24세`
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2014 합계 계 10,3~ 411132 393451 459224 594242 727181
## 2 2014 합계 한국인 10,1~ 404369 389623 456867 587646 701179
## 3 2014 합계 등록외국인~ 266,~ 6763 3828 2357 6596 26002
## 4 2014 종로구~ 계 165,~ 4609 4991 6704 9372 12979
## 5 2014 종로구~ 한국인 156,~ 4445 4886 6622 9004 11384
## 6 2014 종로구~ 등록외국인~ 8,351 164 105 82 368 1595
## 7 2014 중구 계 136,~ 4806 4036 4268 6026 9379
## 8 2014 중구 한국인 128,~ 4582 3874 4148 5754 8497
## 9 2014 중구 등록외국인~ 8,162 224 162 120 272 882
## 10 2014 용산구~ 계 249,~ 9715 9094 9744 12054 15322
## # ... with 302 more rows, and 16 more variables: `25~29세` <dbl>,
## # `30~34세` <dbl>, `35~39세` <dbl>, `40~44세` <dbl>, `45~49세` <dbl>,
## # `50~54세` <dbl>, `55~59세` <dbl>, `60~64세` <dbl>, `65~69세` <dbl>,
## # `70~74세` <dbl>, `75~79세` <dbl>, `80~84세` <dbl>, `85~89세` <dbl>,
## # `90~94세` <dbl>, `95~99세` <dbl>, `100세 이상+` <dbl>
“data_country.xlsx”데이터를보면 어떤 국가의 경우 인구가 0으로 입력되어 있습니다. 0에는 NA로 부여해주세요
data2<-read_xlsx("data_country.xlsx") %>% mutate(POPULATION=ifelse(POPULATION==0,NA,POPULATION))
colSums(is.na(data2)) #단 하나 존재했었네요
## COUNTRY COUNTRY CODE ISO CODES POPULATION AREA KM2
## 0 0 0 1 0
## GDP $USD
## 19