R 데이터 매니지먼트: 기초

R 기본 문법과, 보험공단 샘플 데이터를 이용한 데이터 매니지먼트 방법을 정리하였습니다. 본 내용은 서울대병원 진단검사의학과 선생님들의 교육에 쓰일 예정입니다.

Jinseob Kim https://github.com/jinseob2kim (Zarathu Co.,Ltd)https://www.zarathu.com
03-10-2020

Table of Contents


김진섭 대표는 4월 2일(목) 부터 6회에 걸쳐, 서울대병원 진단검사의학과 의국원들의 통계분석 능력 함양을 위한 맞춤 교육 이라는 주제로 R 교육을 진행할 예정입니다. 1주차 강의록을 미리 공유합니다.

시작하기 전에

R 데이터 매니지먼트 방법은 크게 3 종류가 있다.

  1. 원래의 R 문법을 이용한 방법으로 과거 홈페이지1에 정리했었다.

  2. tidyverse는 직관적인 코드를 작성할 수 있는 점을 장점으로 원래의 R 문법을 빠르게 대체하고 있다. 본 블로그에 정리 내용이 있다.

  3. data.table 패키지는 빠른 실행속도를 장점으로 tidyverse 의 득세 속에서 살아남았으며, 역시 과거 홈페이지2에 정리한 바 있다.

본 강의는 이중 첫 번째에 해당하며 2주차에 tidyverse 를 다룰 것이다. data.table 은 이번 교육에는 포함시키지 않았는데, R에 익숙해지면서 느린 속도가 점점 거슬린다면 data.table 을 시작할 때이다.

실습은 클라우드 환경인 RStudio cloud 를 이용하여 진행한다. 회원가입 후, 아래를 따라 강의자료가 포함된 실습환경을 생성하자.

  1. https://rstudio.cloud 회원 가입
  1. https://rstudio.cloud/spaces/53975/join?access_code=kuFNlbt%2FbSj6DH%2FuppMdXzvU4e1EPrQNgNsFAQBf 들어가서 “Join Space” 클릭
  1. 위쪽 “Projects” 클릭 후, “New Project” 를 눌러 “New Project from Git Repo” 를 선택 후, Repo 주소 https://github.com/jinseob2kim/lecture-snuhlab 입력.
Project 생성

Figure 1: Project 생성

개인 PC에서 실습을 원한다면 http://www.r-project.orghttps://rstudio.com/products/rstudio/download/#download 에서 RRStudio 를 설치하자.

전체 강의 일정

회차 일시 주제
1 4월 2일(목) 11-13시 R 데이터 매니지먼트 기초
2 4월 14일(화) 11-13시 R 데이터 매니지먼트 최근: tidyverse
3 4월 28일(화) 11-13시 R 데이터 시각화: ggplot2
4 5월 12일(화) 11-13시 의학연구에서의 기술통계
5 5월 26일(화) 11-13시 회귀분석, 생존분석
6 6월 9일(화) 11-13시 R로 논문쓰기: rmarkdown

R 기초연산 : 벡터(vector)

R 의 기본 연산단위는 벡터이며, x <- c(1, 2, 3) 은 1,2,3 으로 이루어진 길이 3인 벡터를 x 에 저장한다. 대입연산자는 =<- 둘 다 가능하지만 함수의 인자로도 쓰이는 = 와 구별하기 위해 <- 를 권장한다. 자주 쓰는 연산을 실습하자.


x <- c(1, 2, 3, 4, 5, 6)            ## vector of variable
y <- c(7, 8, 9, 10, 11, 12)
x + y                                  

[1]  8 10 12 14 16 18

x * y

[1]  7 16 27 40 55 72

sqrt(x)                            ## root

[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490

sum(x)                                

[1] 21

diff(x)                            ## difference

[1] 1 1 1 1 1

mean(x)                            ## mean  

[1] 3.5

var(x)                             ## variance

[1] 3.5

sd(x)                              ## standard deviation

[1] 1.870829

median(x)                          ## median

[1] 3.5

IQR(x)                             ## inter-quantile range

[1] 2.5

max(x)                             ## max value

[1] 6

which.max(x)                       ## order of max value

[1] 6

max(x, y)                          ## max value among x & y

[1] 12

length(x)                          

[1] 6

max(x, y) 는 x, y 각각의 최대값이 아닌, 전체에서 최대인 값 1개를 보여줌을 기억하자. 잠시 후 각각의 최대값 구하는 연습문제가 나온다.

벡터에서 특정 항목을 골라내려면 그것의 위치 혹은 조건문을 이용한다.


x[2]                               ## 2 번째

[1] 2

x[-2]                              ## 2 번째만 빼고

[1] 1 3 4 5 6

x[1:3]                             ## 1-3 번째

[1] 1 2 3

x[c(1, 2, 3)]                      ## 동일 

[1] 1 2 3

x[c(1, 3, 4, 5, 6)]                ## 1, 3, 4, 5, 6  번째

[1] 1 3 4 5 6

x >= 4                             ## 각 항목이 4 이상인지 TRUE/FALSE

[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE

sum(x >= 4)                        ## TRUE 1, FALSE 0 인식 

[1] 3

x[x >= 4]                          ## TRUE 인 것들만, 즉 4 이상인 것들         

[1] 4 5 6

sum(x[x >= 4])                     ## 4 이상인 것들만 더하기. 

[1] 15

x %in% c(1, 3, 5)                  ## 1, 3, 5 중 하나에 속하는지 TRUE/FALSE

[1]  TRUE FALSE  TRUE FALSE  TRUE FALSE

x[x %in% c(1, 3, 5)]               

[1] 1 3 5

벡터만들기

seq 로 일정 간격인, rep 로 항목들이 반복되는 벡터를 만들 수 있다.


v1 <- seq(-5, 5, by = .2); v1             ## Sequence

 [1] -5.0 -4.8 -4.6 -4.4 -4.2 -4.0 -3.8 -3.6 -3.4 -3.2 -3.0 -2.8 -2.6
[14] -2.4 -2.2 -2.0 -1.8 -1.6 -1.4 -1.2 -1.0 -0.8 -0.6 -0.4 -0.2  0.0
[27]  0.2  0.4  0.6  0.8  1.0  1.2  1.4  1.6  1.8  2.0  2.2  2.4  2.6
[40]  2.8  3.0  3.2  3.4  3.6  3.8  4.0  4.2  4.4  4.6  4.8  5.0

v2 <- rep(1, 3); v2                       ## Repeat

[1] 1 1 1

v3 <- rep(c(1, 2, 3), 2); v3              ## Repeat for vector

[1] 1 2 3 1 2 3

v4 <- rep(c(1, 2, 3), each = 2); v4       ## Repeat for vector : each

[1] 1 1 2 2 3 3

for, if/else, ifelse

for loop 는 같은 작업을 반복할 때 이용하며 while 도 비슷한 의미이다. 예시를 통해 배워보자.


for (i in 1:3){
  print(i)
}

[1] 1
[1] 2
[1] 3

i <- 0
for (j in c(1, 2, 4, 5, 6)){
  i <- i + j
}
i

[1] 18

ifelse 는 조건문을 다룬다. elseelse if 문은 선행 조건문의 마지막과 같은 줄이어야 함을 기억하자.


x <- 5
if (x >= 3 ){
  x <- x + 3
}
x

[1] 8

x <- 5
if (x >= 10){
  print("High")
} else if (x >= 5){
  print("Medium")
} else {
  print("Low")
}                                          ## else if, else 주의: 반드시 } 와 같은 줄에 위치하도록.

[1] "Medium"

ifelse벡터화if/else 문으로 벡터의 각 항목마다 조건문을 적용하는데, 엑셀의 if 문과 비슷하다.


x <- 1:6
y <- ifelse(x >= 4, "Yes", "No")           ## ifelse (조건,참일때,거짓일때)
y

[1] "No"  "No"  "No"  "Yes" "Yes" "Yes"

함수 만들기

R을 배우는 단계에서는 함수를 만들어 쓸 일이 거의 없겠지만, 결측치 포함된 데이터에서 평균이나 분산을 구할 때 귀찮을 수 있다. R은 결측치가 하나라도 포함되면 평균값, 분산값으로 NA를 출력하기 때문이다. 이를 해결하기 위해서라도 아래처럼 기초 함수 만드는 법은 알고 있는 것이 좋다.


x <- c(1:10, 12, 13, NA, NA, 15, 17)      ## 결측치가 포함되어 있다면..
mean(x)                                           

[1] NA

mean0 <- function(x){
  mean(x, na.rm = T)
}                                         ## mean함수의 na.rm 옵션을 TRUE로 바꿈. default는 F

mean0 <- function(x){mean(x, na.rm = T)}  ## 한줄에 쓸 수도 있다. 
mean0(x)

[1] 8

둘 이상의 변수를 포함한 함수도 다음과 같이 만들 수 있다.


twomean <- function(x1, x2){
  a <- (x1 + x2)/2
  a
}
twomean(4, 6)

[1] 5

Apply 문 : apply, sapply, lapply

벡터를 다루는 연산을 잘 활용하면, 벡터의 각 항목에 대해 for loop 을 쓰는 것보다 간편하게 코드를 작성할 수 있다. 행렬에서 행마다 평균을 구하는 예를 살펴보자.


mat <- matrix(1:20, nrow = 4, byrow = T)   ## 4행 5열, byrow = T : 행부터 채운다. 
mat

     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    6    7    8    9   10
[3,]   11   12   13   14   15
[4,]   16   17   18   19   20

모든 행에 대해 for loop 을 이용, 평균을 구하여 저장하는 코드는 아래와 같다.


out <- NULL                                ## 빈 벡터, 여기에 하나씩 붙여넣는다.
for (i in 1:nrow(mat)){
  out <- c(out, mean(mat[i, ]))
}
out

[1]  3  8 13 18

처음에 빈 벡터를 만들고 여기에 결과를 붙여가는 모습이 번거로워 보인다. sapply 또는 lapply 를 사용하면 행 또는 열 단위 연산을 간단히 수행할 수 있다.


sapply(1:nrow(mat), function(x){mean(mat[x, ])})             ## Return vector

[1]  3  8 13 18

lapply(1:nrow(mat), function(x){mean(mat[x, ])})             ## Return list type

[[1]]
[1] 3

[[2]]
[1] 8

[[3]]
[1] 13

[[4]]
[1] 18

unlist(lapply(1:nrow(mat), function(x){mean(mat[x, ])}))     ## Same to sapply

[1]  3  8 13 18

처음에 빈 벡터를 만들고, 이어붙이는 과정이 생략되어 간단한 코드가 되었다. list 는 벡터보다 상위개념으로 모든 것을 담을 수 있는 큰 그릇에 비유할 수 있는데, 본 강의에서는 unlist 를 취하면 벡터나 행렬을 얻게 된다는 정도만 언급하고 넘어가겠다. 사실 행렬의 행/열 단위 연산은 apply 혹은 row***, col*** 시리즈의 함수가 따로 있어, 더 간단히 이용할 수 있다.


apply(mat, 1, mean)                                          ## 1: 행

[1]  3  8 13 18

rowMeans(mat)                                                ## 동일

[1]  3  8 13 18

rowSums(mat)                                                 ## 행별로 합

[1] 15 40 65 90

apply(mat, 2, mean)                                          ## 2: 열

[1]  8.5  9.5 10.5 11.5 12.5

colMeans(mat)                                                ## 열별로 합

[1]  8.5  9.5 10.5 11.5 12.5

연습문제 1

sapplylapply를 이용하여, 아래 두 벡터의 최대값을 각각 구하여라.


x <- 1:6
y <- 7:12
정답 보기

lapply(list(x, y), max)

[[1]]
[1] 6

[[2]]
[1] 12

  sapply(list(x, y), max)

[1]  6 12


멀티코어 병렬연산으로 apply 를 빠르게 수행할 수도 있는데 본 강의에서는 생략한다. 궁금하신 분은 과거 정리 내용 을 참고하기 바란다.

데이터 불러와서 작업하기

이제부터는 실제 데이터를 읽어서 그 데이터를 매니징 하는 방법을 배워보도록 하겠다.

데이터 불러오기, 저장하기

데이터를 불러오기 전에 미리 디렉토리를 지정하면 그 다음부터는 편하게 읽고 쓸 수 있다.


getwd()                                                     ## 현재 디렉토리 

[1] "/home/js/Homepage/blog/_posts/2020-02-16-rdatamanagement-basic"

setwd("data")                                               ## 디렉토리 설정
## 동일
setwd("/home/js/Homepage/blog/_posts/2020-02-16-rdatamanagement-basic/data")
getwd()

[1] "/home/js/Homepage/blog/_posts/2020-02-16-rdatamanagement-basic/data"

폴더 구분을 / 로 해야 한다는 점을 명심하자 (\\ 도 가능). R 은 유닉스 기반이기 때문이다. 이제 실습 데이터를 읽어볼텐데, 가급적이면 데이터 포맷은 csv로 만드는 것을 추천한다. 콤마로 분리된 가장 간단한 형태로, 용량도 작고 어떤 소프트웨어 에서도 읽을 수 있기 때문이다. 물론 Excel, SPSS, SAS 파일도 읽을 수 있는데, 변수명이나 값에 한글이 있으면 encoding 에러가 생길 수 있으므로 미리 처리하자.


ex <- read.csv("example_g1e.csv")
head(ex)

URL 링크를 이용할 수도 있다.


ex <- read.csv("https://raw.githubusercontent.com/jinseob2kim/lecture-snuhlab/master/data/example_g1e.csv")

Excel 파일은 readxl, SAS나 SPSS는 haven 패키지를 이용한다.


#install.packages(c("readxl", "haven"))                    ## install packages    
library(readxl)                                            ## for xlsx
ex.excel <- read_excel("example_g1e.xlsx", sheet = 1)      ## 1st sheet

library(haven)                                             ## for SAS/SPSS/STATA   
ex.sas <- read_sas("example_g1e.sas7bdat")                 ## SAS
ex.spss <- read_sav("example_g1e.sav")                     ## SPSS
head(ex.spss)

아래와 같이 Excel, SAS, SPSS 데이터는 read.csv 와 형태가 좀 달라보인다. 이것은 최근 R에서 인기있는 tidyverse 스타일의 데이터인데, 자세한 내용은 다음 강의에서 다룰 예정이니 일단 넘어가자.

파일 저장은 write.csv 를 이용하며, 맨 왼쪽에 나타나는 행 넘버링을 빼려면 row.names = F 옵션을 추가한다.


write.csv(ex, "example_g1e_ex.csv", row.names = F)

haven 패키지에서 write_saswrite_sav 도 가능하다.


write_sas(ex.sas, "example_g1e_ex.sas7bdat")
write_sav(ex.spss, "example_g1e_ex.sav")

읽은 데이터 살펴보기

본격적으로 데이터를 살펴보자. 데이터는 09-15년 공단 건강검진 데이터에서 실습용으로 32 명을 뽑은 자료이며, 자세한 내용은 “data/2교시 테이블 세부 레이아웃 소개(최신자료).pdf” 를 참고하자.

데이터 살펴보기

head 로 처음 6줄, tail 로 마지막 6줄을 볼 수 있다. 데이터 간단히 확인하려고 쓰인다.


head(ex)                                                   ## 처음 6행
tail(ex)                                                   ## 마지막 6행
head(ex, 10)                                               ## 처음 10행

strhead 와는 다른 방식으로 데이터를 확인한다. int 는 정수, num 은 실수형을 의미한다.


str(ex)

'data.frame':   1644 obs. of  32 variables:
 $ EXMD_BZ_YYYY  : int  2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 ...
 $ RN_INDI       : int  562083 334536 911867 183321 942671 979358 554112 487160 793017 219397 ...
 $ HME_YYYYMM    : int  200909 200911 200903 200908 200909 200912 200911 200908 200906 200912 ...
 $ Q_PHX_DX_STK  : int  0 0 0 NA NA NA NA NA NA 0 ...
 $ Q_PHX_DX_HTDZ : int  0 0 0 NA NA NA NA NA NA 0 ...
 $ Q_PHX_DX_HTN  : int  1 0 0 NA NA NA NA NA NA 1 ...
 $ Q_PHX_DX_DM   : int  0 0 0 NA NA NA NA NA NA 0 ...
 $ Q_PHX_DX_DLD  : int  0 0 0 NA NA NA NA NA NA 0 ...
 $ Q_PHX_DX_PTB  : int  NA NA NA NA NA NA NA NA NA NA ...
 $ Q_HBV_AG      : int  3 2 3 3 3 2 2 3 3 3 ...
 $ Q_SMK_YN      : int  1 1 1 1 1 1 1 1 1 1 ...
 $ Q_DRK_FRQ_V09N: int  0 0 0 0 0 0 0 0 0 0 ...
 $ HGHT          : int  144 162 163 152 159 157 160 159 156 146 ...
 $ WGHT          : int  61 51 65 51 50 55 56 54 53 48 ...
 $ WSTC          : int  90 63 82 70 73 73 67 66 67 78 ...
 $ BMI           : num  29.4 19.4 24.5 22.1 19.8 22.3 21.9 21.4 21.8 22.5 ...
 $ VA_LT         : num  0.7 0.8 0.7 0.8 0.7 1.5 1.5 1.2 1.2 1.5 ...
 $ VA_RT         : num  0.8 1 0.6 0.9 0.8 1.5 1.5 1.5 1 1.5 ...
 $ BP_SYS        : int  120 120 130 101 132 110 119 111 138 138 ...
 $ BP_DIA        : int  80 80 80 62 78 70 78 60 72 84 ...
 $ URN_PROT      : int  1 1 1 1 1 1 1 1 1 1 ...
 $ HGB           : num  12.6 13.8 15 13.1 13 11.9 11.2 12.2 11 12.8 ...
 $ FBS           : int  117 96 118 90 92 100 84 88 74 107 ...
 $ TOT_CHOL      : int  264 169 216 199 162 192 152 166 155 178 ...
 $ TG            : int  128 92 132 100 58 109 38 42 86 87 ...
 $ HDL           : int  60 70 55 65 40 53 43 58 52 35 ...
 $ LDL           : int  179 80 134 114 111 117 101 99 85 125 ...
 $ CRTN          : num  0.9 0.9 0.8 0.9 0.9 0.7 0.8 1 0.6 0.7 ...
 $ SGOT          : int  25 18 26 18 24 15 8 16 15 21 ...
 $ SGPT          : int  20 15 30 14 23 12 6 11 13 21 ...
 $ GGT           : int  25 28 30 11 15 14 10 12 13 23 ...
 $ GFR           : int  59 74 79 61 49 83 97 65 96 70 ...

names 로 변수들 이름을 확인할 수 있다. 공백이나 특수문자는 “.” 로 바뀌고, 이름이 같은 변수들은 뒤에 숫자가 추가되어 구별된다. read.csv(..., check.names = F) 옵션으로 원래 이름을 유지할 수 있으나 에러의 원인이 되므로 추천하지 않는다.


names(ex)

 [1] "EXMD_BZ_YYYY"   "RN_INDI"        "HME_YYYYMM"    
 [4] "Q_PHX_DX_STK"   "Q_PHX_DX_HTDZ"  "Q_PHX_DX_HTN"  
 [7] "Q_PHX_DX_DM"    "Q_PHX_DX_DLD"   "Q_PHX_DX_PTB"  
[10] "Q_HBV_AG"       "Q_SMK_YN"       "Q_DRK_FRQ_V09N"
[13] "HGHT"           "WGHT"           "WSTC"          
[16] "BMI"            "VA_LT"          "VA_RT"         
[19] "BP_SYS"         "BP_DIA"         "URN_PROT"      
[22] "HGB"            "FBS"            "TOT_CHOL"      
[25] "TG"             "HDL"            "LDL"           
[28] "CRTN"           "SGOT"           "SGPT"          
[31] "GGT"            "GFR"           

샘플수, 변수 갯수는 dim, nrow, ncol 로 확인한다.


dim(ex)                                                    ## row, column

[1] 1644   32

nrow(ex)                                                   ## row

[1] 1644

ncol(ex)                                                   ## column

[1] 32

클래스는 class로 확인한다. read.csvdata.frame, Excel/SAS/SPSS 는 tibble & `data.frame 인데, data.frame행렬이면서 데이터에 특화된 list, tibble 은 앞서 언급했던 tidyverse 스타일의 data.frame 인 정도만 알고 넘어가자.


class(ex)

[1] "data.frame"

class(ex.spss)

[1] "tbl_df"     "tbl"        "data.frame"

summary 로 모든 변수들의 평균, 중위수, 결측치 등을 한 번에 확인할 수 있다. R은 결측치를 NA 로 표시하며, 안타깝지만 분산은 나오지 않는다.


summary(ex)

  EXMD_BZ_YYYY     RN_INDI          HME_YYYYMM      Q_PHX_DX_STK   
 Min.   :2009   Min.   :   2270   Min.   :200901   Min.   :0.0000  
 1st Qu.:2010   1st Qu.: 230726   1st Qu.:201011   1st Qu.:0.0000  
 Median :2012   Median : 487160   Median :201210   Median :0.0000  
 Mean   :2012   Mean   : 490782   Mean   :201216   Mean   :0.0112  
 3rd Qu.:2014   3rd Qu.: 726101   3rd Qu.:201406   3rd Qu.:0.0000  
 Max.   :2015   Max.   :1010623   Max.   :201512   Max.   :1.0000  
                                                   NA's   :573     
 Q_PHX_DX_HTDZ     Q_PHX_DX_HTN   Q_PHX_DX_DM      Q_PHX_DX_DLD   
 Min.   :0.0000   Min.   :0.00   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:0.0000   1st Qu.:0.00   1st Qu.:0.0000   1st Qu.:0.0000  
 Median :0.0000   Median :0.00   Median :0.0000   Median :0.0000  
 Mean   :0.0241   Mean   :0.25   Mean   :0.0693   Mean   :0.0399  
 3rd Qu.:0.0000   3rd Qu.:0.25   3rd Qu.:0.0000   3rd Qu.:0.0000  
 Max.   :1.0000   Max.   :1.00   Max.   :1.0000   Max.   :1.0000  
 NA's   :566      NA's   :492    NA's   :547      NA's   :566     
  Q_PHX_DX_PTB       Q_HBV_AG        Q_SMK_YN     Q_DRK_FRQ_V09N 
 Min.   :0.0000   Min.   :1.000   Min.   :1.000   Min.   :0.000  
 1st Qu.:0.0000   1st Qu.:2.000   1st Qu.:1.000   1st Qu.:0.000  
 Median :0.0000   Median :2.000   Median :1.000   Median :1.000  
 Mean   :0.0276   Mean   :2.235   Mean   :1.632   Mean   :1.026  
 3rd Qu.:0.0000   3rd Qu.:3.000   3rd Qu.:2.000   3rd Qu.:2.000  
 Max.   :1.0000   Max.   :3.000   Max.   :3.000   Max.   :7.000  
 NA's   :703      NA's   :2       NA's   :2       NA's   :6      
      HGHT            WGHT            WSTC             BMI       
 Min.   :134.0   Min.   : 31.0   Min.   : 57.00   Min.   :12.30  
 1st Qu.:158.0   1st Qu.: 56.0   1st Qu.: 74.00   1st Qu.:21.50  
 Median :165.0   Median : 64.0   Median : 81.00   Median :23.70  
 Mean   :164.5   Mean   : 65.1   Mean   : 80.69   Mean   :23.92  
 3rd Qu.:171.0   3rd Qu.: 73.0   3rd Qu.: 87.00   3rd Qu.:26.20  
 Max.   :188.0   Max.   :118.0   Max.   :114.00   Max.   :37.20  
                                                                 
     VA_LT           VA_RT            BP_SYS          BP_DIA     
 Min.   :0.100   Min.   :0.1000   Min.   : 81.0   Min.   : 49.0  
 1st Qu.:0.800   1st Qu.:0.7000   1st Qu.:110.0   1st Qu.: 70.0  
 Median :1.000   Median :1.0000   Median :120.0   Median : 78.0  
 Mean   :0.984   Mean   :0.9792   Mean   :122.3   Mean   : 76.6  
 3rd Qu.:1.200   3rd Qu.:1.2000   3rd Qu.:130.0   3rd Qu.: 82.0  
 Max.   :9.900   Max.   :9.9000   Max.   :180.0   Max.   :120.0  
                                                                 
    URN_PROT          HGB             FBS            TOT_CHOL    
 Min.   :1.000   Min.   : 5.90   Min.   : 61.00   Min.   : 68.0  
 1st Qu.:1.000   1st Qu.:12.90   1st Qu.: 86.00   1st Qu.:170.0  
 Median :1.000   Median :14.10   Median : 94.00   Median :193.0  
 Mean   :1.078   Mean   :14.11   Mean   : 97.23   Mean   :194.9  
 3rd Qu.:1.000   3rd Qu.:15.40   3rd Qu.:103.00   3rd Qu.:218.0  
 Max.   :5.000   Max.   :18.30   Max.   :290.00   Max.   :363.0  
 NA's   :4                                                       
       TG              HDL             LDL              CRTN        
 Min.   :  13.0   Min.   : 23.0   Min.   :  19.0   Min.   : 0.4000  
 1st Qu.:  72.0   1st Qu.: 46.0   1st Qu.:  90.0   1st Qu.: 0.8000  
 Median : 106.0   Median : 54.0   Median : 112.0   Median : 0.9000  
 Mean   : 134.9   Mean   : 55.9   Mean   : 118.7   Mean   : 0.9891  
 3rd Qu.: 163.0   3rd Qu.: 64.0   3rd Qu.: 134.0   3rd Qu.: 1.0000  
 Max.   :1210.0   Max.   :593.0   Max.   :8100.0   Max.   :16.5000  
                                  NA's   :16                        
      SGOT            SGPT             GGT              GFR        
 Min.   :  6.0   Min.   :  3.00   Min.   :  6.00   Min.   :  3.00  
 1st Qu.: 19.0   1st Qu.: 15.00   1st Qu.: 16.00   1st Qu.: 76.00  
 Median : 23.0   Median : 20.00   Median : 24.50   Median : 87.00  
 Mean   : 25.6   Mean   : 25.98   Mean   : 36.34   Mean   : 89.74  
 3rd Qu.: 28.0   3rd Qu.: 30.00   3rd Qu.: 41.00   3rd Qu.:101.00  
 Max.   :459.0   Max.   :779.00   Max.   :408.00   Max.   :196.00  
                                                   NA's   :467     

특정 변수 보기

data.frame 에서 특정변수는 $ 를 이용, 데이터이름$변수이름 로 확인할 수 있다. 앞서 언급했듯이 data.frame 은 행렬과 list의 성질도 갖고 있어 해당 스타일로도 가능하다.


ex$EXMD_BZ_YYYY                                            ## data.frame style
ex[, "EXMD_BZ_YYYY"]                                       ## matrix style
ex[["EXMD_BZ_YYYY"]]                                       ## list style
ex[, 1]                                                    ## matrix style with order
ex[[1]]                                                    ## list style with order

2개 이상 변수선택은 행렬 스타일을 이용한다.


ex[, c("EXMD_BZ_YYYY", "RN_INDI", "BMI")]                  ## matrix syle with names
ex[, c(1, 2, 16)]                                          ## matrix syle with names
ex[, names(ex)[c(1, 2, 16)]]                               ## same

특정 변수는 벡터형태로 나타나므로 처음에 다루었던 벡터다루기를 그대로 활용할 수 있다. 예를 들어 년도 변수인 EXMD_BZ_YYYY의 첫 50개만 확인하면 아래와 같다.


ex$EXMD_BZ_YYYY[1:50]                                      ## data.frame style
ex[1:50, 1]                                                ## matrix style
ex[[1]][1:50]                                              ## list style

 [1] 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009
[14] 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009
[27] 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009
[40] 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009 2009

unique 로 변수가 어떤 값들로 이루어져 있는지, table 로 해당 값들이 몇개씩 있는지 확인한다.


unique(ex$EXMD_BZ_YYYY)                                   ## unique value

[1] 2009 2010 2011 2012 2013 2014 2015

length(unique(ex$EXMD_BZ_YYYY))                           ## number of unique value

[1] 7

table(ex$EXMD_BZ_YYYY)                                    ## table

2009 2010 2011 2012 2013 2014 2015 
 214  236  223  234  243  254  240 

새로운 변수 만들기

연속형 변수인 BMI 에서 원하는 조건에 맞는 정보를 뽑아내는 연습을 해 보자.


mean(ex$BMI)                                              ## mean

[1] 23.92257

BMI_cat <- (ex$BMI >= 25)                                 ## TRUE of FALSE
table(BMI_cat)                         

BMI_cat
FALSE  TRUE 
 1077   567 

rows <- which(ex$BMI >= 25)                               ## row numbers
head(rows)                                      

[1]  1 14 18 21 23 24

values <- ex$BMI[ex$BMI >= 25]                            ## values
head(values)

[1] 29.4 27.5 27.7 28.0 30.7 25.6

length(values)

[1] 567

BMI_HGHT_and <- (ex$BMI >= 25 & ex$HGHT >= 175)              ## and
BMI_HGHT_or <- (ex$BMI >= 25 | ex$HGHT >= 175)               ## or

데이터에 새로운 변수로 추가하는 방법은 간단하다.


ex$zero <- 0                                              ## variable with 0
ex$BMI_cat <- (ex$BMI >= 25)                              ## T/F
ex$BMI_cat <- as.integer(ex$BMI >= 25)                    ## 0, 1
ex$BMI_cat <- as.character(ex$BMI >= 25)                  ## "0", "1"
ex$BMI_cat <- ifelse(ex$BMI >= 25, "1", "0")              ## same
table(ex$BMI_cat)

   0    1 
1077  567 

ex[, "BMI_cat"] <- (ex$BMI >= 25)                         ## matrix style
ex[["BMI_cat"]] <- (ex$BMI >= 25)                         ## list style

변수 클래스 설정: 데이터 읽은 후 가장 먼저 해야할 것.

앞서 데이터의 클래스가 data.frame 임을 언급했었는데, 각 변수들도 자신의 클래스를 갖으며 대표적인 것이 숫자형(numeric), 문자형(character), 팩터(factor) 이다. 그 외 T/F 로 나타내는 논리(logical), 날짜를 나타내는 Date 클래스가 있다. 숫자는 integer(정수), numeric(실수) 이 있는데, 전부 실수형(numeric)으로 해도 상관없어 설명은 생략한다. 범주형은 characterfactor 두 종류가 있는데, 전자는 단순 문자인 반면 후자는 레벨(level) 이 있어 reference 나 순서를 설정할 수 있다. read.csv 로 읽으면 숫자는 int/num, 문자는 전부 factor 가 기본값이므로, 숫자 변수 중 0/1 같은 것들은 직접 factor 로 바꿔줘야 한다. ID와 설문조사 변수를 범주형으로 바꿔보자.


vars.cat <- c("RN_INDI", "Q_PHX_DX_STK", "Q_PHX_DX_HTDZ", "Q_PHX_DX_HTN", "Q_PHX_DX_DM", "Q_PHX_DX_DLD", "Q_PHX_DX_PTB", 
              "Q_HBV_AG", "Q_SMK_YN", "Q_DRK_FRQ_V09N")
vars.cat <- names(ex)[c(2, 4:12)]                              ## same
vars.cat <- c("RN_INDI", grep("Q_", names(ex), value = T))     ## same: extract variables starting with "Q_"

vars.conti <- setdiff(names(ex), vars.cat)                     ## Exclude categorical variables
vars.conti <- names(ex)[!(names(ex) %in% vars.cat)]            ## same: !- not, %in%- including

for (vn in vars.cat){                                          ## for loop: as.factor
  ex[, vn] <- as.factor(ex[, vn])
}

for (vn in vars.conti){                                        ## for loop: as.numeric
  ex[, vn] <- as.numeric(ex[, vn])
}

summary(ex)

  EXMD_BZ_YYYY     RN_INDI       HME_YYYYMM     Q_PHX_DX_STK
 Min.   :2009   4263   :   7   Min.   :200901   0   :1059   
 1st Qu.:2010   38967  :   7   1st Qu.:201011   1   :  12   
 Median :2012   56250  :   7   Median :201210   NA's: 573   
 Mean   :2012   84322  :   7   Mean   :201216               
 3rd Qu.:2014   99917  :   7   3rd Qu.:201406               
 Max.   :2015   115809 :   7   Max.   :201512               
                (Other):1602                                
 Q_PHX_DX_HTDZ Q_PHX_DX_HTN Q_PHX_DX_DM Q_PHX_DX_DLD Q_PHX_DX_PTB
 0   :1052     0   :864     0   :1021   0   :1035    0   :915    
 1   :  26     1   :288     1   :  76   1   :  43    1   : 26    
 NA's: 566     NA's:492     NA's: 547   NA's: 566    NA's:703    
                                                                 
                                                                 
                                                                 
                                                                 
 Q_HBV_AG    Q_SMK_YN   Q_DRK_FRQ_V09N      HGHT      
 1   :  77   1   :995   0      :805    Min.   :134.0  
 2   :1102   2   :256   1      :379    1st Qu.:158.0  
 3   : 463   3   :391   2      :249    Median :165.0  
 NA's:   2   NA's:  2   3      :121    Mean   :164.5  
                        4      : 28    3rd Qu.:171.0  
                        (Other): 56    Max.   :188.0  
                        NA's   :  6                   
      WGHT            WSTC             BMI            VA_LT      
 Min.   : 31.0   Min.   : 57.00   Min.   :12.30   Min.   :0.100  
 1st Qu.: 56.0   1st Qu.: 74.00   1st Qu.:21.50   1st Qu.:0.800  
 Median : 64.0   Median : 81.00   Median :23.70   Median :1.000  
 Mean   : 65.1   Mean   : 80.69   Mean   :23.92   Mean   :0.984  
 3rd Qu.: 73.0   3rd Qu.: 87.00   3rd Qu.:26.20   3rd Qu.:1.200  
 Max.   :118.0   Max.   :114.00   Max.   :37.20   Max.   :9.900  
                                                                 
     VA_RT            BP_SYS          BP_DIA         URN_PROT    
 Min.   :0.1000   Min.   : 81.0   Min.   : 49.0   Min.   :1.000  
 1st Qu.:0.7000   1st Qu.:110.0   1st Qu.: 70.0   1st Qu.:1.000  
 Median :1.0000   Median :120.0   Median : 78.0   Median :1.000  
 Mean   :0.9792   Mean   :122.3   Mean   : 76.6   Mean   :1.078  
 3rd Qu.:1.2000   3rd Qu.:130.0   3rd Qu.: 82.0   3rd Qu.:1.000  
 Max.   :9.9000   Max.   :180.0   Max.   :120.0   Max.   :5.000  
                                                  NA's   :4      
      HGB             FBS            TOT_CHOL           TG        
 Min.   : 5.90   Min.   : 61.00   Min.   : 68.0   Min.   :  13.0  
 1st Qu.:12.90   1st Qu.: 86.00   1st Qu.:170.0   1st Qu.:  72.0  
 Median :14.10   Median : 94.00   Median :193.0   Median : 106.0  
 Mean   :14.11   Mean   : 97.23   Mean   :194.9   Mean   : 134.9  
 3rd Qu.:15.40   3rd Qu.:103.00   3rd Qu.:218.0   3rd Qu.: 163.0  
 Max.   :18.30   Max.   :290.00   Max.   :363.0   Max.   :1210.0  
                                                                  
      HDL             LDL              CRTN              SGOT      
 Min.   : 23.0   Min.   :  19.0   Min.   : 0.4000   Min.   :  6.0  
 1st Qu.: 46.0   1st Qu.:  90.0   1st Qu.: 0.8000   1st Qu.: 19.0  
 Median : 54.0   Median : 112.0   Median : 0.9000   Median : 23.0  
 Mean   : 55.9   Mean   : 118.7   Mean   : 0.9891   Mean   : 25.6  
 3rd Qu.: 64.0   3rd Qu.: 134.0   3rd Qu.: 1.0000   3rd Qu.: 28.0  
 Max.   :593.0   Max.   :8100.0   Max.   :16.5000   Max.   :459.0  
                 NA's   :16                                        
      SGPT             GGT              GFR              zero  
 Min.   :  3.00   Min.   :  6.00   Min.   :  3.00   Min.   :0  
 1st Qu.: 15.00   1st Qu.: 16.00   1st Qu.: 76.00   1st Qu.:0  
 Median : 20.00   Median : 24.50   Median : 87.00   Median :0  
 Mean   : 25.98   Mean   : 36.34   Mean   : 89.74   Mean   :0  
 3rd Qu.: 30.00   3rd Qu.: 41.00   3rd Qu.:101.00   3rd Qu.:0  
 Max.   :779.00   Max.   :408.00   Max.   :196.00   Max.   :0  
                                   NA's   :467                 
    BMI_cat      
 Min.   :0.0000  
 1st Qu.:0.0000  
 Median :0.0000  
 Mean   :0.3449  
 3rd Qu.:1.0000  
 Max.   :1.0000  
                 

summary 를 보면 설문조사 변수들이 처음과 달리 빈도로 요약됨을 알 수 있다. 한 가지 주의할 점은 factor 를 numeric 으로 바로 바꾸면 안된다는 것이다. 방금 factor 로 바꾼 Q_PHX_DX_STK 를 numeric 으로 바꿔서 테이블로 요약하면, 원래의 0/1 이 아닌 1/2로 바뀐다.


table(
  as.numeric(ex$Q_PHX_DX_STK)
  )

   1    2 
1059   12 

factor를 바로 바꾸면 원래 값이 아닌, factor에 내장된 레벨(순서값) 로 바뀌기 때문이다. 제대로 바꾸려면 아래처럼 character 로 먼저 바꿔준 후 숫자형을 적용해야 한다.


table(
  as.numeric(as.character(ex$Q_PHX_DX_STK))
  )

   0    1 
1059   12 

마지막으로 Date 클래스를 살펴보자. 검진년월 변수인 HME_YYYYMM 를 Date 로 바꿔 볼텐데, Date는 년/월/일 이 모두 필요하므로 일은 1로 통일하고 paste 로 붙이겠다.


addDate <- paste(ex$HME_YYYYMM, "01", sep = "")                ## add day- use `paste`
ex$HME_YYYYMM <- as.Date(addDate, format = "%Y%m%d")           ## set format                  
head(ex$HME_YYYYMM)

[1] "2009-09-01" "2009-11-01" "2009-03-01" "2009-08-01" "2009-09-01"
[6] "2009-12-01"

class(ex$HME_YYYYMM)

[1] "Date"

결측치 다루기

변수 클래스만큼 중요한 것이 결측치 처리이다. 앞서 “함수만들기” 에서 봤듯이 결측치가 있으면 평균같은 기본적인 계산도 na.rm = T 옵션이 필요하다. 결측치가 있는 LDL 변수의 평균을 연도별로 구해보자. 그룹별 통계는 tapply 를 이용한다.


tapply(ex$LDL, ex$EXMD_BZ_YYYY, mean)                          ## measure/group/function
2009 2010 2011 2012 2013 2014 2015
150.9486 NA NA NA NA NA NA

2009년만 결측치가 없고, 나머지는 결측치가 있어 평균값이 NA 로 나온다.na.rm = T 옵션으로 결측치를 제외하면 원하는 결과를 얻는다.


tapply(ex$LDL, ex$EXMD_BZ_YYYY, 
       function(x){
         mean(x, na.rm = T)
         })    

    2009     2010     2011     2012     2013     2014     2015 
150.9486 112.9914 112.9450 117.5259 111.1577 116.5455 111.5294 

더 큰 문제는, 대부분의 R 통계분석이 결측치를 갖는 샘플을 분석에서 제외한다는 점이다. 그래서 결측치를 신경쓰지 않고 분석하다보면, 원래 샘플 수와 분석에 이용된 샘플 수가 달라지는 문제가 생길 수 있다. LDLHDL 의 회귀분석 결과를 예로 살펴보자.


summary(lm(LDL ~ HDL, data = ex))

Call:
lm(formula = LDL ~ HDL, data = ex)

Residuals:
   Min     1Q Median     3Q    Max 
-103.8  -28.2   -6.6   15.4 7974.7 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 138.2747    15.2318   9.078   <2e-16 ***
HDL          -0.3499     0.2570  -1.362    0.174    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 201.9 on 1626 degrees of freedom
  (16 observations deleted due to missingness)
Multiple R-squared:  0.001139,  Adjusted R-squared:  0.0005244 
F-statistic: 1.854 on 1 and 1626 DF,  p-value: 0.1735

“16 observations deleted due to missingness” 라는 글자가 보일 것이다. LDL 이 결측인 16명은 분석에서 제외했다는 뜻이다.

연습문제 2: 결측치 처리

결측치를 처리하는 제일 간단한 방법은 “하나라도 결측치 있는 샘플은 제외” 로, na.omit 함수를 이용하면 된다.


ex.naomit <- na.omit(ex)
nrow(ex.naomit)

[1] 620

1644 명에서 620 명으로 샘플 수가 줄어든 것을 확인할 수 있다. 필자는 보통 결측치 처리에 다음의 3가지 원칙을 적용한다.

  1. 결측치 너무 많으면(예: 10% 이상) 그 변수는 삭제

  2. 연속변수는 중간값(median)

  3. 범주형변수는 최빈값(mode)

이제 문제이다. 아까 변수형을 정리한 ex 데이터에 위 3가지 원칙을 적용, 새로운 데이터 ex.impute 을 만들어 보아라. 단 최빈값 함수는 아래와 같이 getmode 로 주어진다.


getmode <- function(v){
   uniqv <- unique(v)
   uniqv[which.max(tabulate(match(v, uniqv)))]
}

getmode(ex$Q_PHX_DX_STK)

[1] 0
Levels: 0 1
정답 보기

vars.ok <- sapply(names(ex), function(v){sum(is.na(ex[, v])) < nrow(ex)/10})
ex.impute <- ex[, vars.ok]                                     ## only missing < 10%

for (v in names(ex.impute)){
  if (is.factor(ex.impute[, v])){                              ## or class(ex[, v]) == "factor"
    ex.impute[, v] <- ifelse(is.na(ex.impute[, v]), 
                             getmode(ex.impute[, v]), 
                             ex.impute[, v])
  } else if (is.numeric(ex[, v])){                             ## or class(ex[, v]) %in% c("integer", "numeric")
    ex.impute[, v] <- ifelse(is.na(ex.impute[, v]), 
                             median(ex.impute[, v], na.rm = T), 
                             ex.impute[, v])
  } else{                                                      ## when date
    ex.impute[, v]
  }
}

summary(ex.impute)

  EXMD_BZ_YYYY     RN_INDI        HME_YYYYMM            Q_HBV_AG    
 Min.   :2009   Min.   :  1.0   Min.   :2009-01-01   Min.   :1.000  
 1st Qu.:2010   1st Qu.:133.8   1st Qu.:2010-11-01   1st Qu.:2.000  
 Median :2012   Median :275.0   Median :2012-10-01   Median :2.000  
 Mean   :2012   Mean   :272.7   Mean   :2012-08-31   Mean   :2.235  
 3rd Qu.:2014   3rd Qu.:405.2   3rd Qu.:2014-06-01   3rd Qu.:3.000  
 Max.   :2015   Max.   :547.0   Max.   :2015-12-01   Max.   :3.000  
    Q_SMK_YN     Q_DRK_FRQ_V09N       HGHT            WGHT      
 Min.   :1.000   Min.   :1.000   Min.   :134.0   Min.   : 31.0  
 1st Qu.:1.000   1st Qu.:1.000   1st Qu.:158.0   1st Qu.: 56.0  
 Median :1.000   Median :2.000   Median :165.0   Median : 64.0  
 Mean   :1.631   Mean   :2.023   Mean   :164.5   Mean   : 65.1  
 3rd Qu.:2.000   3rd Qu.:3.000   3rd Qu.:171.0   3rd Qu.: 73.0  
 Max.   :3.000   Max.   :8.000   Max.   :188.0   Max.   :118.0  
      WSTC             BMI            VA_LT           VA_RT       
 Min.   : 57.00   Min.   :12.30   Min.   :0.100   Min.   :0.1000  
 1st Qu.: 74.00   1st Qu.:21.50   1st Qu.:0.800   1st Qu.:0.7000  
 Median : 81.00   Median :23.70   Median :1.000   Median :1.0000  
 Mean   : 80.69   Mean   :23.92   Mean   :0.984   Mean   :0.9792  
 3rd Qu.: 87.00   3rd Qu.:26.20   3rd Qu.:1.200   3rd Qu.:1.2000  
 Max.   :114.00   Max.   :37.20   Max.   :9.900   Max.   :9.9000  
     BP_SYS          BP_DIA         URN_PROT          HGB       
 Min.   : 81.0   Min.   : 49.0   Min.   :1.000   Min.   : 5.90  
 1st Qu.:110.0   1st Qu.: 70.0   1st Qu.:1.000   1st Qu.:12.90  
 Median :120.0   Median : 78.0   Median :1.000   Median :14.10  
 Mean   :122.3   Mean   : 76.6   Mean   :1.078   Mean   :14.11  
 3rd Qu.:130.0   3rd Qu.: 82.0   3rd Qu.:1.000   3rd Qu.:15.40  
 Max.   :180.0   Max.   :120.0   Max.   :5.000   Max.   :18.30  
      FBS            TOT_CHOL           TG              HDL       
 Min.   : 61.00   Min.   : 68.0   Min.   :  13.0   Min.   : 23.0  
 1st Qu.: 86.00   1st Qu.:170.0   1st Qu.:  72.0   1st Qu.: 46.0  
 Median : 94.00   Median :193.0   Median : 106.0   Median : 54.0  
 Mean   : 97.23   Mean   :194.9   Mean   : 134.9   Mean   : 55.9  
 3rd Qu.:103.00   3rd Qu.:218.0   3rd Qu.: 163.0   3rd Qu.: 64.0  
 Max.   :290.00   Max.   :363.0   Max.   :1210.0   Max.   :593.0  
      LDL              CRTN              SGOT            SGPT       
 Min.   :  19.0   Min.   : 0.4000   Min.   :  6.0   Min.   :  3.00  
 1st Qu.:  90.0   1st Qu.: 0.8000   1st Qu.: 19.0   1st Qu.: 15.00  
 Median : 112.0   Median : 0.9000   Median : 23.0   Median : 20.00  
 Mean   : 118.6   Mean   : 0.9891   Mean   : 25.6   Mean   : 25.98  
 3rd Qu.: 134.0   3rd Qu.: 1.0000   3rd Qu.: 28.0   3rd Qu.: 30.00  
 Max.   :8100.0   Max.   :16.5000   Max.   :459.0   Max.   :779.00  
      GGT              zero      BMI_cat      
 Min.   :  6.00   Min.   :0   Min.   :0.0000  
 1st Qu.: 16.00   1st Qu.:0   1st Qu.:0.0000  
 Median : 24.50   Median :0   Median :0.0000  
 Mean   : 36.34   Mean   :0   Mean   :0.3449  
 3rd Qu.: 41.00   3rd Qu.:0   3rd Qu.:1.0000  
 Max.   :408.00   Max.   :0   Max.   :1.0000  


Subset

특정 조건을 만족하는 서브데이터는 지금까지 배웠던 것을 응용해 만들 수도 있지만, subset 함수가 편하다. 아래는 2012 이후의 자료만 뽑는 예시이다. 이제부터는 결측치를 전부 제외한 ex.naomit 데이터를 이용하겠다.


ex1 <- ex.naomit                                               ## simple name
ex1.2012 <- ex1[ex1$EXMD_BZ_YYYY >= 2012, ]
table(ex1.2012$EXMD_BZ_YYYY)

2012 2013 2014 2015 
 151  162  154  153 

ex1.2012 <- subset(ex1, EXMD_BZ_YYYY >= 2012)                  ## subset
table(ex1.2012$EXMD_BZ_YYYY)

2012 2013 2014 2015 
 151  162  154  153 

그룹별 통계

결측치 다루기에서 그룹별 통계를 구할 때 tapply 를 이용했었다. tapply 를 여러 변수, 여러 그룹을 동시에 고려도록 확장할 수 있는 함수가 aggregate 로, 허리둘레와 BMI의 평균을 고혈압 또는 당뇨 여부에 따라 살펴보자.


aggregate(ex1[, c("WSTC", "BMI")], list(ex1$Q_PHX_DX_HTN), mean)
aggregate(cbind(WSTC, BMI) ~ Q_PHX_DX_HTN, data = ex1, mean)   ## same
Group.1 WSTC BMI
0 80.35687 23.85592
1 84.48958 25.11771
Q_PHX_DX_HTN WSTC BMI
0 80.35687 23.85592
1 84.48958 25.11771

결측치가 있어도 잘 적용된다는 장점이 있다.


aggregate(cbind(WSTC, BMI) ~ Q_PHX_DX_HTN, data = ex, mean)
Q_PHX_DX_HTN WSTC BMI
0 80.23958 23.70961
1 83.87847 24.99861

당뇨여부도 그룹으로 다루려면 list 에 추가하면 된다.


aggregate(ex1[, c("WSTC", "BMI")], list(ex1$Q_PHX_DX_HTN, ex1$Q_PHX_DX_DM), mean)
Group.1 Group.2 WSTC BMI
0 0 80.23107 23.82990
1 0 83.93976 25.17952
0 1 87.55556 25.34444
1 1 88.00000 24.72308

Group.1 이 첫번째 그룹은 고혈압 여부, Group.2 가 두번째 그룹인 당뇨 여부이다. 위와 마찬가지로 formula 형태를 이용할 수도 있다.


aggregate(cbind(WSTC, BMI) ~ Q_PHX_DX_HTN + Q_PHX_DX_DM, data = ex1, mean)
Q_PHX_DX_HTN Q_PHX_DX_DM WSTC BMI
0 0 80.23107 23.82990
1 0 83.93976 25.17952
0 1 87.55556 25.34444
1 1 88.00000 24.72308

표준편차를 같이 보려면 function(x){c(mean = mean(x), sd = sd(x))} 와 같이 원하는 함수들을 벡터로 모으면 된다.


aggregate(cbind(WSTC, BMI) ~ Q_PHX_DX_HTN + Q_PHX_DX_DM, data = ex1, function(x){c(mean = mean(x), sd = sd(x))})
Q_PHX_DX_HTN Q_PHX_DX_DM WSTC BMI
0 0 80.231068 9.546884
1 0 83.939759 9.124277
0 1 87.555556 7.551674
1 1 88.000000 6.177918

아예 데이터의 모든 변수의 평균을 다 볼순 없을까? 아래처럼 “.” 으로 전체 데이터를 지정할 수 있다.


aggregate(. ~ Q_PHX_DX_HTN  + Q_PHX_DX_DM, data = ex1, function(x){c(mean = mean(x), sd = sd(x))})    

  Q_PHX_DX_HTN Q_PHX_DX_DM EXMD_BZ_YYYY.mean EXMD_BZ_YYYY.sd
1            0           0       2013.493204        1.109498
2            1           0       2013.578313        1.105645
3            0           1       2013.333333        1.414214
4            1           1       2013.307692        1.031553
  RN_INDI.mean RN_INDI.sd HME_YYYYMM.mean HME_YYYYMM.sd
1    269.30680  159.12594      16102.3184      422.8574
2    251.78313  154.03951      16121.8072      413.1641
3    269.77778   92.88807      16036.3333      551.2248
4    303.53846  142.18686      16018.6923      417.4666
  Q_PHX_DX_STK.mean Q_PHX_DX_STK.sd Q_PHX_DX_HTDZ.mean
1        1.00776699      0.08787296         1.00194175
2        1.01204819      0.10976426         1.06024096
3        1.00000000      0.00000000         1.00000000
4        1.07692308      0.27735010         1.07692308
  Q_PHX_DX_HTDZ.sd Q_PHX_DX_DLD.mean Q_PHX_DX_DLD.sd
1       0.04406526         1.0174757       0.1311630
2       0.23937916         1.0722892       0.2605404
3       0.00000000         1.0000000       0.0000000
4       0.27735010         1.0769231       0.2773501
  Q_PHX_DX_PTB.mean Q_PHX_DX_PTB.sd Q_HBV_AG.mean Q_HBV_AG.sd
1         1.0271845       0.1627787     2.2291262   0.5236863
2         1.0000000       0.0000000     2.1927711   0.5512255
3         1.0000000       0.0000000     2.0000000   0.0000000
4         1.0769231       0.2773501     2.2307692   0.4385290
  Q_SMK_YN.mean Q_SMK_YN.sd Q_DRK_FRQ_V09N.mean Q_DRK_FRQ_V09N.sd
1     1.6970874   0.8674234           2.0388350         1.3329287
2     1.3855422   0.6777172           1.9759036         1.3612754
3     1.6666667   0.8660254           1.8888889         0.3333333
4     1.5384615   0.6602253           1.9230769         1.1151636
   HGHT.mean    HGHT.sd WGHT.mean   WGHT.sd WSTC.mean   WSTC.sd
1 166.613592   9.116636 66.582524 13.211630 80.231068  9.546884
2 160.506024   9.254364 65.313253 13.155661 83.939759  9.124277
3 168.333333  10.185774 71.777778  8.913161 87.555556  7.551674
4 162.384615   9.639662 65.076923  6.211032 88.000000  6.177918
   BMI.mean    BMI.sd VA_LT.mean  VA_LT.sd VA_RT.mean  VA_RT.sd
1 23.829903  3.276315  1.0190291 0.5248189  1.0079612 0.3503677
2 25.179518  3.693922  0.8469880 0.3201895  0.8638554 0.3444962
3 25.344444  2.711140  0.9111111 0.1691482  0.8111111 0.2368778
4 24.723077  2.057164  0.7769231 0.2350668  0.9000000 0.1080123
  BP_SYS.mean  BP_SYS.sd BP_DIA.mean BP_DIA.sd URN_PROT.mean
1  119.889320  13.378266   75.452427  9.464616     1.0543689
2  132.879518  14.344539   81.481928 11.015910     1.2168675
3  128.555556   8.647415   83.333333 11.842719     1.0000000
4  129.461538  12.149180   79.307692  7.846280     1.0769231
  URN_PROT.sd   HGB.mean     HGB.sd  FBS.mean    FBS.sd TOT_CHOL.mean
1   0.3430173 14.3749515  1.5952305  94.75534  12.71807     196.96505
2   0.6634756 14.1048193  1.6682036 103.60241  14.34330     191.30120
3   0.0000000 15.2555556  1.0284832 131.11111  19.62425     169.77778
4   0.2773501 13.4153846  0.9711215 125.30769  34.11838     179.61538
  TOT_CHOL.sd   TG.mean     TG.sd  HDL.mean    HDL.sd  LDL.mean
1    34.20684 132.80777 107.56421 54.943689 12.881333 118.49903
2    32.64769 138.09639  81.93106 55.903614 16.123468 108.22892
3    47.79325 164.66667  68.40870 46.333333  9.394147  92.77778
4    39.92397 154.76923 139.23072 48.769231 10.288779 102.30769
     LDL.sd CRTN.mean   CRTN.sd SGOT.mean   SGOT.sd SGPT.mean
1  50.86475 0.8871845 0.1867988 24.151456  9.426161 24.609709
2  29.09167 0.9168675 0.2483208 25.289157  6.400324 22.963855
3  38.88694 0.9666667 0.2549510 35.777778 25.849457 47.666667
4  28.39420 0.9153846 0.1675617 31.769231 19.689904 36.307692
    SGPT.sd GGT.mean   GGT.sd GFR.mean   GFR.sd zero.mean zero.sd
1 16.616090 34.47573 31.35216 92.01359 19.14246         0       0
2  9.671993 35.77108 31.18799 81.16867 17.75430         0       0
3 44.235167 44.22222 21.01058 87.55556 22.20423         0       0
4 27.417709 48.46154 66.83265 80.69231 14.34332         0       0
  BMI_cat.mean BMI_cat.sd
1    0.3223301  0.4678230
2    0.4337349  0.4986022
3    0.5555556  0.5270463
4    0.1538462  0.3755338

Sort

정렬은 순위함수인 order 를 이용한다. 기본은 오름차순이며, 내림차순을 원한다면 (-) 붙인 값의 순위를 구하면 된다.


ord <- order(ex1$HGHT)                                        ## 작은 순서대로 순위
head(ord)

[1] 500 168   3 328 473 177

head(ex1$HGHT[ord])                                           ## Sort

[1] 138 139 140 140 141 143

ord.desc <- order(-ex1$HGHT)                                  ## descending
head(ex1$HGHT[ord.desc])

[1] 188 186 185 185 184 183

ex1.sort <- ex1[ord, ]
head(ex1.sort)

Wide to long, long to wide format

받은 데이터가 원하는 형태가 아닌 경우가 있다. 수축기 혈압을 10번 측정해서 각각 SBP1, SBP2, …, SBP10 변수에 기록된 데이터를 본다면, 이것들을 쫙 아래로 내려 측정시기, 측정값 2개의 변수로 정리하고 싶다는 마음이 들 것이다. 이럴 때 쓰는 함수가 melt, 반대로 데이터를 옆으로 늘릴 때 쓰는 함수가 dcast 이다(Figure 23).

melt and dcast

Figure 2: melt and dcast

실습으로 수축기/이완기 혈압 변수를 합쳐서 아래로 내려보자.


library(reshape2)
long <- melt(ex1, id = c("EXMD_BZ_YYYY", "RN_INDI"), measure.vars = c("BP_SYS", "BP_DIA"), variable.name = "BP_type", value.name = "BP")
long

id 는 유지할 변수, measure.vars 는 내릴 변수를 의미하고, variable.name, value.name 은 각각 그룹, 값의 변수이름을 의미한다. 이를 원래대로 되돌리려면 dcast 를 이용하는데, “유지할 변수 ~ 펼칠 변수” 형태로 formula 를 입력한다.


wide <- dcast(long, EXMD_BZ_YYYY + RN_INDI ~ BP_type, value.var = "BP")
head(wide)

Merge

merge 함수를 이용한다. “by” 옵션으로 기준이 되는 공통 컬럼을 설정하며, 기준 컬럼의 이름이 두 데이터 셋에서 다른 경우는 “by.x”“by.y” 로 따로 설정한다. 실습을 위해 ex1 데이터를 2개로 나눈 후 merge 를 적용하겠다.


ex1.Q <- ex1[, c(1:3, 4:12)]
ex1.measure <- ex1[, c(1:3, 13:ncol(ex1))]
head(ex1.Q)
head(ex1.measure)

전자는 설문조사 결과를, 후자는 측정값을 포함했고 “년도, ID, 검진년월” 은 공통변수이다. 이 공통변수로 merge 를 적용하면


ex1.merge <- merge(ex1.Q, ex1.measure, by = c("EXMD_BZ_YYYY", "RN_INDI", "HME_YYYYMM"), all = T)
head(ex1.merge)

합쳐진 원래 데이터를 얻을 수 있다. all = T 는 한 쪽에만 있는 샘플을 유지하는 옵션이며 빈 변수는 NA 로 채워진다. 공통인 샘플만 취하려면 all = F 로 바꾸자.

마치며

이번 강의를 정리하자.

  1. RStudio cloud 로 클라우드 환경에서 실습을 진행했으며

  2. 기초 벡터연산과 for, if, ifelse, 함수만들기, apply 문을 통해 기본 문법을 익혔고

  3. 공단 검진 데이터를 실습자료를 읽어와 데이터를 살펴보는 법을 배웠다.

    • 변수 생성, 클래스 설정, 결측치 처리, 서브데이터, 그룹별 통계, 정렬
  4. 마지막으로 Long/wide type 데이터 변환과 merge 를 다루었다.

기타 기본적으로 알아야 할 R 명령어는 아래의 Base R Cheat Sheet 에서 확인할 수 있다.


다음 강의에서는 쉬운 문법으로 R 의 대세가 된 tidyverse 를 다룰 예정인데, 오늘 배운 기본 문법과 많은 비교가 될 것이다. 미리 알아보고 싶은 분은 본 블로그의 이전 글4 을 참고하기 바란다.


  1. https://jinseob2kim.github.io/rbasic.html↩︎

  2. https://jinseob2kim.github.io/radv1.html↩︎

  3. https://t1.daumcdn.net/cfile/tistory/2433F13D55E1163907↩︎

  4. https://blog.zarathu.com/posts/2019-01-03-rdatamanagement↩︎

Corrections

If you see mistakes or want to suggest changes, please create an issue on the source repository.

Reuse

Text and figures are licensed under Creative Commons Attribution CC BY 4.0. Source code is available at https://github.com/zarathucorp/blog, unless otherwise noted. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".

Citation

For attribution, please cite this work as

Kim (2020, March 10). Zarathu Blog: R 데이터 매니지먼트: 기초. Retrieved from https://blog.zarathu.com/posts/2020-02-16-rdatamanagement-basic/

BibTeX citation

@misc{kim2020r,
  author = {Kim, Jinseob},
  title = {Zarathu Blog: R 데이터 매니지먼트: 기초},
  url = {https://blog.zarathu.com/posts/2020-02-16-rdatamanagement-basic/},
  year = {2020}
}