728x90
반응형

 이전 포스트에서 R의 기본 함수를 사용해 결측 값을 다뤄보았다. 이번에는 결측 값 문제를 해결하는데 특화된 패키지인 naniar, VIM 패키지를 사용해서 결측 값을 보다 체계적으로 다뤄보도록 하자.

 

 

외부 패키지를 이용해서 결측 값을 다뤄보자.

  • R 기본 함수만으로도 결측 값을 파악하는데 큰 지장이 없긴 하지만, 결측 값을 위해 특화된 패키지들을 이용해서, 보다 단순하게 결측 값을 파악할 수도 있다.
  • 사용할 패키지들을 설치하고, library 하여 분석 준비를 해보자.
  • 학습에 사용할 데이터는 mlbench 패키지에 있는 BostonHousing 데이터와 moonBook 패키지의 acs 데이터다
  • mlbench 패키지의 BostonHousing: 다양한 기계 학습 벤치마킹을 위한 데이터가 있는 패키지로, BostonHousing은 보스턴의 주택 가격에 대한 데이터다.
  • moonBook 패키지의 acs: 의료 데이터가 주로 들어 있으며, acs는 환자의 데이터로, 요골동맥의 혈관 내 초음파 데이터인 radial 등이 있다.
# naniar 패키지 설치
>>> install.packages("naniar")
>>> install.packages("VIM")

# 학습용 데이터가 담긴 Packge
>>> install.packages("mlbench")
>>> install.packages("moonBook")

# 사용할 패키지 library
>>> library("naniar")
>>> library("VIM")
>>> library("moonBook")
>>> library("mlbench")


# 데이터 생성
>>> data("BostonHousing")
>>> data("acs")

# 원본 유지를 위해 사용할 변수에 Data를 담아놓음.
>>> Boston_df = BostonHousing
>>> acs_df = acs

 

 

 

 

1. naniar 패키지의 결측 값 기술 통계량

  • naniar 패키지를 사용하면, 결측 값의 기술 통계량을 보다 편하게 구할 수 있다.
  • 대상 데이터에 임의로 결측 값을 부여해보자.
# sample 함수를 사용하여 ptratio, rad 변수의 임의의 위치에 결측값을 생성하였다.
Boston_df[sample(1:nrow(Boston_df), 30, replace = FALSE), "ptratio"] <- NA
Boston_df[sample(1:nrow(Boston_df), 50, replace = FALSE), "rad"] <- NA
  • sample(x, size, replace = FALSE): 데이터의 전체 수만큼의 연속된 벡터(index와 동일한 벡터)에 원하는 크기만큼 sample을 랜덤 하게 추출했다. replace = FALSE로 두어 비 복원 추출을 실시했다.
# 0.대상 데이터 안에 결측값이 존재하는지 확인
>>> any_na(Boston_df)
[1] TRUE

>>> any_na(Boston_df$zn)
[1] FALSE

>>> any_na(Boston_df$ptratio)
[1] TRUE



# 1.대상 데이터의 결측값에 대한 Boolean값 반환
>>> are_na(Boston_df[1:30,"ptratio"])
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE
[14] FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE
[27] FALSE FALSE FALSE FALSE



# 2.대상 데이터 안에 결측값의 갯수 반환
>>> n_miss(Boston_df)
[1] 80

>>> n_miss(Boston_df$ptratio)
[1] 30



# 3.대상 데이터 안에 결측값의 비율 반환
>>> prop_miss(Boston_df)
[1] 0.01129305

>>> prop_miss(Boston_df$ptratio)
[1] 0.05928854



# 4.대상 데이터에서 결측값이 아닌 값의 수
>>> n_complete(Boston_df)
[1] 7004

>>> n_complete(Boston_df$ptratio)
[1] 476



# 5. 데이터 프레임 내 결측값의 빈도표 출력
>>> miss_var_summary(Boston_df)
# A tibble: 14 x 3
   variable n_miss pct_miss
   <chr>     <int>    <dbl>
 1 rad          50     9.88
 2 ptratio      30     5.93
 3 crim          0     0   
 4 zn            0     0   
 5 indus         0     0   
 6 chas          0     0   
 7 nox           0     0   
 8 rm            0     0   
 9 age           0     0   
10 dis           0     0   
11 tax           0     0   
12 b             0     0   
13 lstat         0     0   
14 medv          0     0   



# 6. 데이터 프레임 내 결측값의 누적 빈도 출력
>>> miss_var_cumsum(Boston_df)
# A tibble: 14 x 3
   variable n_miss n_miss_cumsum
   <chr>     <int>         <int>
 1 crim          0             0
 2 zn            0             0
 3 indus         0             0
 4 chas          0             0
 5 nox           0             0
 6 rm            0             0
 7 age           0             0
 8 dis           0             0
 9 rad          50            50
10 tax           0            50
11 ptratio      30            80
12 b             0            80
13 lstat         0            80
14 medv          0            80
  • any_na(x): 데이터에 결측 값이 존재하는지 Boolean으로 출력
  • are_na(x): 데이터 내 결측 값은 TRUE로 결측 값이 아닌 값은 FALSE로 출력
  • prop_miss(x): 데이터 내 결측 값의 비율
  • n_complete(x): 데이터 내 결측 값이 아닌 데이터의 수
  • miss_var_summary(x): 데이터 프레임의 결측 값 빈도표 출력
  • miss_var_sumsum(x): 데이터 프레임의 결측 값 누적 빈도 표 출력

 

 

 

 

2. 중복 결측 값 보기

  • 각 변수 당, 결측 값의 양이 적다할지라도, 한 데이터 셋 안에 있는 결측 값의 양은 굉장히 많을 수 있다.
  • 만약 한 모델 안에 m개(m≥2)의 변수가 들어가는 경우, 그 모델은 m개 변수의 결측 값을 모두 가정하지 않으면, 잘못된 결과를 도출할 위험이 있다.
  • 때문에 원하는 변수에서 결측 값이 몇 개나 중복되는지를 알아야 한다.
  • 결측 값의 중복량 파악은 Boolean을 이용하면 쉽게 할 수 있다.
# DataFrame 상태에서 apply, Boolean, sum의 성질을 이용
>>> table(apply(is.na.data.frame(Boston_df), MARGIN = 1, sum))
  0   1   2 
431  70   5 


# Matrix로 변환하여 행의 합인 rowSums() 사용.
>>> table(rowSums(as.matrix(is.na.data.frame(Boston_df))))
  0   1   2 
431  70   5 
  • 위 방법을 통해 쉽게 중복된 결측 값의 수를 알 수 있고, 그로 인해 최대로 제거될 변수의 수를 알 수 있다.
  • 그러나, 어떤 변수들에서 결측 값이 중복되는지를 파악하긴 어렵다.
  • 때문에 결측 값 시각화를 통해, 변수별 결측 값의 분포를 볼 필요가 있다.

 

 

 

 

3. 간단한 결측값 시각화

  • 데이터의 크기가 크고, 결측 값의 양이 많다면, 결측 값의 분포를 파악하기 힘들다.
  • 시각화를 통해 결측 값 데이터가 어떻게 생겼는지 본다면, 어떠한 데이터들에 결측 값이 모여있는지를 보기 쉽고, 그로 인해 결측 값을 감안한 표본 축소나 
  • moonBook의 acs 데이터는 본래 결측 값이 존재하는 데이터이므로, 이 데이터를 사용하여, 결측 값 분포를 보도록 하자.
  • naniar 패키지 설치 시, 함께 설치되는 패키지인 visdat에는 vis_miss()라는 결측 값 시각화 함수가 있다.
# 시각화
vis_miss(acs_df)

  • vis_miss(x) 함수를 이용하면, 쉽게 데이터 안에 결측 값이 어떻게 분포해있는지 알 수 있다.
  • 그러나, 결측 값이 있는 행이 흩어진 상태로 나오므로, 보기 조금 어려울 수 있다.
# 시각화
vis_miss(acs_df, cluster = TRUE)

  • vis_miss(x, cluster = TRUE): cluster 파라미터를 TRUE로 잡으면, 공통된 결측 값이 있는 행들을 Cluster로 잡아주므로, 더 쉽게 데이터를 파악할 수 있다.

 

 

 

 

4. VIM 패키지를 사용한 결측 값 시각화

  • vis_miss()는 코드가 매우 쉽지만, 기능이 많지 않다는 단점이 있다.
  • 만약, 데이터의 결측 값을 보다 심도 깊게 보고자 한다면, VIM 패키지를 사용하면 된다.
  • VIM은 Visualization and Imputation of Missing Values의 약자로, 말 그대로 결측 값의 시각화와 결측값 대체에 특화된 패키지라고 할 수 있다.

 

4.1. 중복된 결측 값의 분포

# VIM을 사용한 시각화
aggr(acs, col=c("white", "red"), prop=FALSE, number=TRUE, sortVars = TRUE,
     cex.axis=.8, gap=1, ylab=c("Histogram of NA", "Pattern"))

  • 기능이 보다 많다 보니, 파라미터가 많은데, 그 내용은 다음과 같다.
  • col = c("white", "red"): 결측 값이 없는 셀, 있는 셀의 색깔
  • prop = FALSE: 비율로 출력할지(TRUE), 빈도로 출력할지(FALSE)
  • number = TRUE: 결측 값의 개수를 숫자로 출력할지 여부
  • sortVars = TRUE: 결측 값의 개수로 정렬함
  • cex.axis = .8: 글자 크기
  • gap = 1, 두 그래프의 간격
  • ylab = c("title1", "title2"):  그래프의 이름
  • 위 그래프에서 좌측 그래프는 단순한 히스토그램이니 설명은 생략하도록 하겠다.
  • 우측 그래프는 공통된 결측 값의 빈도를 나타낸다. 예를 들어 EF, height, BMI, weight은 공통 결측 값을 47개 가지고 있다.

 

 

 

 지금까지 외부 라이브러리를 사용하여, 결측 값을 보다 효과적으로 파악하는 방법을 알아보았다. 다음 포스트에서는 결측 값을 채워 넣는 방법인 Single Imputation에 대해 알아보도록 하겠다.

728x90
반응형
728x90
반응형

R 기본 함수로 결측값 다루기

0. 결측값을 다뤄볼 test Dataset을 만들어보자.

# test Dataset
row_number = 20
ID_vt = seq(1, row_number)
ID_vt = paste0("A", ID_vt)

math_vt = sample(10:100, row_number)
english_vt = sample(10:100, row_number)
science_vt = sample(10:100, row_number)
korean_vt = sample(10:100, row_number)

df = data.frame(ID = ID_vt, math = math_vt, english = english_vt,
                science = science_vt, korean = korean_vt)

df[c(10,16), c("math")] = NA
df[c(4,6,10,19), c("english")] = NA
df[c(10,20), c("science")] = NA
df[c(4,15,16,20), c("korean")] = NA

df
##      ID math english science korean
##  1   A1   66      94      78     53
##  2   A2   89      87      74     82
##  3   A3   36      58      44     60
##  4   A4   96      NA      21     NA
##  5   A5   37      96      63     19
##  6   A6   63      NA      45     94
##  7   A7   80      14      79     20
##  8   A8   75      19      56     66
##  9   A9   35      44      59     14
##  10 A10   NA      NA      NA     35
##  11 A11   72      92      27     39
##  12 A12   74      79      96     76
##  13 A13   98      80      10     10
##  14 A14   11      28      87     62
##  15 A15   44      90      20     NA
##  16 A16   NA      24      35     NA
##  17 A17   88      12      55     22
##  18 A18   58      25      40     81
##  19 A19   67      NA      48     25
##  20 A20   50      75      NA     NA
  • 무작위 위치에 결측값을 넣어주었다.

 

 

 

1. 결측값 확인하기

함수 의미
is.na(x) data에 속한 NA를 True로 나타낸다.
na.fail(x) dasta에 NA가 포함되어 있으면 Error를 반환한다.
na.omit(x) data에 NA가 포함되어 있으면 이를 제외한다.
na.pass(x) data에 NA가 포함되어 있더라도 무시하고 진행한다.
complete.case(x) data에서 결측값이 없는 행만 반환한다.
  • is.na() 함수와 table() 함수를 혼합해서 사용해보면 보다 재밌는 결과가 나온다.

 

# 데이터 전체를 NA 기준으로 TRUE, FALSE를 반환
is.na(df)
##           ID  math english science korean
##  [1,] FALSE FALSE   FALSE   FALSE  FALSE
##  [2,] FALSE FALSE   FALSE   FALSE  FALSE
##  [3,] FALSE FALSE   FALSE   FALSE  FALSE
##  [4,] FALSE FALSE    TRUE   FALSE   TRUE
##  [5,] FALSE FALSE   FALSE   FALSE  FALSE
##  [6,] FALSE FALSE    TRUE   FALSE  FALSE
##  [7,] FALSE FALSE   FALSE   FALSE  FALSE
##  [8,] FALSE FALSE   FALSE   FALSE  FALSE
##  [9,] FALSE FALSE   FALSE   FALSE  FALSE
## [10,] FALSE  TRUE    TRUE    TRUE  FALSE
## [11,] FALSE FALSE   FALSE   FALSE  FALSE
## [12,] FALSE FALSE   FALSE   FALSE  FALSE
## [13,] FALSE FALSE   FALSE   FALSE  FALSE
## [14,] FALSE FALSE   FALSE   FALSE  FALSE
## [15,] FALSE FALSE   FALSE   FALSE   TRUE
## [16,] FALSE  TRUE   FALSE   FALSE   TRUE
## [17,] FALSE FALSE   FALSE   FALSE  FALSE
## [18,] FALSE FALSE   FALSE   FALSE  FALSE
## [19,] FALSE FALSE    TRUE   FALSE  FALSE
## [20,] FALSE FALSE   FALSE    TRUE   TRUE
  • 전체 Data 안에서 결측값 여부를 보여준다.
# 데이터 내, 다수 변수들의 결측값을 한 번에 확인
# 출력된 결과는 각 변수별 결측값의 수이다.
colSums(is.na(df))
## ID    math english science  korean 
##  0       2       4       2       4
  • 가장 쉬운 코드로 결측값의 상태를 보여준다.
# 데이터 내 총 결측값의 수
table(is.na(df))
## FALSE  TRUE 
##    88    12
sum(is.na(df))
## [1] 12
  • 둘 모두 총 결측값의 수이다.
# 특정 열의 결측치 확인
table(is.na(df$math))
## FALSE  TRUE 
##    18     2
  • 특정 열에서 결측값이 몇 개 있는지 반환한다.
# 결측값이 전체 데이터 셋에서 차지하는 비중
mean(is.na(df))
## [1] 0.12
  • 전체 데이터에서 결측값이 차지하는 비중으로 dataframe$column을 하여 특정 컬럼에 대해서만 확인할 수도 있다.

 

 

 

 

2. 결측값이 있는 행 제거하기

  • 데이터 내에서 결측값이 있는 모든 행을 제거하는 방법과 변수별 결측값이 있는 행만 취사 선택하여 제거하는 방법이 있다.
  • 전체 결측값을 제거하는 경우는 아래와 같다.
# 결측치가 없는 행만 반환1
na.omit(df)
##     ID math english science korean
## 1   A1   55      15      63     52
## 2   A2   79      73      95     21
## 3   A3   33      84      66     49
## 5   A5   15      35      78     91
## 7   A7   80      45      67    100
## 8   A8   91      34      97     84
## 9   A9   85      51      61     23
## 11 A11   63      65      43     88
## 12 A12   13      83      32     12
## 13 A13   99      39      69     64
## 14 A14   76      46      83     96
## 17 A17   11      80      31     33
## 18 A18   68      92      12     24
# 결측치가 없는 행만 반환2
df[complete.cases(df),]
##     ID math english science korean
## 1   A1   47      94      37     54
## 2   A2   61      62      25     94
## 3   A3   96      40      83     49
## 5   A5   63      81      11     43
## 7   A7   90      41      85     88
## 8   A8   70      30      49     19
## 9   A9   97      84      90     22
## 11 A11   80      76      52     14
## 12 A12   84      22      41     66
## 13 A13   16      58      93     61
## 14 A14   75      21      30     45
## 17 A17   51      56      55     71
## 18 A18   37      53      84     33
  • na.omit()과 complete.cases()는 유사한 기능을 한다.
  • 특정 변수의 결측값만 제거하는 방법은 다음과 같다.
# math 컬럼의 결측값을 제외하고 가져오자
df %>% filter(!is.na(math))
##     ID math english science korean
## 1   A1   15      36      69     12
## 2   A2   94     100      74     58
## 3   A3   71      94      32     62
## 4   A4   93      NA      34     NA
## 5   A5   48      58      46     52
## 6   A6   64      NA      92     77
## 7   A7   40      16      72     40
## 8   A8   86      86      77     19
## 9   A9   81      89      28     63
## 10 A11   47      91      94     93
## 11 A12   97      27      45     70
## 12 A13   14      63      62     59
## 13 A14   61      72      50     49
## 14 A15   92      30      65     NA
## 15 A17   78      78      13     44
## 16 A18   68      99      15     86
## 17 A19   30      NA      68     48
## 18 A20   72      66      NA     NA
  • dplyr은 R에서 기본으로 제공하는 함수는 아니지만, Data Handling에서 필수인 함수이므로 한번 짚고 넘어가겠다.
df[complete.cases(df[,"math"]),]
##     ID math english science korean
## 1   A1   15      36      69     12
## 2   A2   94     100      74     58
## 3   A3   71      94      32     62
## 4   A4   93      NA      34     NA
## 5   A5   48      58      46     52
## 6   A6   64      NA      92     77
## 7   A7   40      16      72     40
## 8   A8   86      86      77     19
## 9   A9   81      89      28     63
## 11 A11   47      91      94     93
## 12 A12   97      27      45     70
## 13 A13   14      63      62     59
## 14 A14   61      72      50     49
## 15 A15   92      30      65     NA
## 17 A17   78      78      13     44
## 18 A18   68      99      15     86
## 19 A19   30      NA      68     48
## 20 A20   72      66      NA     NA
  • na.omit()처럼 모든 결측값을 한번에 제거하는 방법은, 분석에서 중요하지 않은 변수의 결측값의 존재로 인해 실제 사라지지 않아도 되는 결측값까지 모두 제거해버릴 위험이 있다.
  • 그러므로, na.omit()과 같은 결측값 제거 방법을 사용하기 전에 분석 대상이 될 변수와 표본 축소가 필요한 경우(예를 들어 비흡연자만 대상으로 하여 흡연 관련 변수와 흡연자를 분석 대상에서 제외함) 이를 먼저 실시하여 Data를 1차 정리하고 결측값 제거를 하도록 하자.

 

 

3. 결측값이 있는 열의 연산

  • 결측값이 데이터에 포함돼 있는 경우, 이를 바로 연산하면 결과가 NA로 출력된다.
  • 결측값이 있는 행을 제외하고 연산하고자 한다면 na.rm 파라미터에 True로 전달하면 된다.
# math 열에 있는 결측값만 제외하고 가져와보자
mean(df$math, na.rm = TRUE)
## [1] 60.44444
# math 컬럼의 결측값을 제외하고 mean을 계산해보자
library(dplyr)
df %>% summarise(mean_N = mean(math, na.rm = TRUE))
## [1] 60.44444

 

  •  결측값을 다룰 때 가장 중요한 것은 분석 대상 집단을 정하는 것으로, 분석 목표에 필요한 변수를 모두 가져오며, 결측값이 존재하지 않는 대상 집단을 명확히 해야한다.
  •  이 부분은 초보 분석가들이 하기 가장 좋은 실수 중 하나로 변수 A의 결측값과 변수 B의 결측값이 서로 다르게 제거된 상태에서 평균을 비롯한 각종 연산 값을 도출하면, 서로 다른 집단에 대한 연산값이 도출되는 결과가 나올 수 있다.
  •  그러므로, na.rm 인자를 이용해 연산하기보다는 분석 대상 집단을 명확히 하여 결측값을 모두 제거한 후 연산하기를 바란다.

 

 

 지금까지 R의 기본 함수를 이용해서 결측값을 다루는 법에 대해 학습해보았다. 다음 포스트에서는 결측값을 다루는 데 있어서 가장 유용한 라이브러리 중 하나인 naniar 패키지를 활용해서 결측값을 다루는 법을 알아보자.

728x90
반응형
728x90
반응형

지난 포스트에선 결측 값의 종류에 대해 살펴보았다. 이번 포스트에서는 발생한 결측 값들이 무작위 결측(MAR)이란 가정하에 결측 값을 대체하는 방법에 대해 알아보도록 하겠다.

 

 

결측 값 대체하기 - Single imputation

 발생한 결측 값이 어떠한 사유에 의해 발생한 것으로 추정되는 상태인 비 무작위 결측(NMAR)이 아니라면, 다음과 같은 비교적 단순한 방법으로 결측 값을 대체할 수 있다.

 

 

1. 완전제거법(Listwise deletion)

  • 결측치가 포함된 케이스를 분석 대상에서 완전히 제거하는 방법
  • SPSS, SAS, STATA와 같은 각종 통계 툴에서 기본값(Default)으로 설정돼있는 방법이다.
  • 발생한 결측값이 완전 임의 결측(MCAR)인 경우, 해당 방법을 사용해도 전체 집단을 대표할 수 있으며, 분석의 편향(bias)이 발생하지도 않는다.
  • 한계점
    1. 그러나, 완전 임의 결측(MCAR)이 발생할 확률은 굉장히 낮으므로, 완전제거법으로 만들어진 데이터는 전체 표본을 대변하지 못하고, 편향을 일으킬 수 있다. (Stuart, E. A., Azur, M, Frangakis, C. & Leadf, P.(2009). “Multiple Imputation with Large Data Sets: A Case Study of the Children’s Mental Health Initiative, ”American Journal of Epidemiology, 169(9): 1133-1139.)
    2. 각 변수의 결측치가 완전임의결측이라 일지라도, 서로서로 결측이 있는 행이 달라, 완전 제거법 사용 후, 표본 수가 급격하게 감소할 수 있다.
    3. 정보의 손실과 검정력 약화 문제가 발생할 수 있다.

 

 

 

2. 평균대체법(Mean substitution)

  • 어떤 변수의 결측값을, 관측된 값의 평균값으로 대체
  • 평균 대체법은 특정 정보가 부재할 경우, 평균이 가장 좋은 대푯값이라는 논리에 기인하는 방법
  • 한계점
    1. 정보량 손실을 줄인다는 관점에서는 완전 제거법에 비해 향상된 방법이라고 할 수는 있으나, 여전히 결측치 대체에 불확실성을 고려하지 못한다는 단점이 있다.
    2. 결측치 대신 평균값을 넣기 때문에, 변수의 분산이 감소되고, 공분산과 상관계수의 혼란도 발생할 가능성이 있다. 이로 인해 평균 대체를 가장 좋지 않은 대체방법이라고도 한다.
    3. 예를 들어, 실제 수학 성적의 평균 값은 65점이나, 점수가 매우 낮은 사람들이 자신의 성적을 기재하지 않아 결측 값을 제외한 평균값인 80점을 결측 값 대신 넣는다면, 관측값의 평균에 결측 값 데이터들이 모두 모이게 되고, 그로 인해 분산이 크게 왜곡될 수 있다.

 

 

 

3. 회귀대체법(Regression-based single inputation)

  • Osbome, J. W. (2012). Best Practices in Dataa Cleaning: A Complete Guide to Everything You need to Do Before and After Collecting Your Data. CH 6. Thousand Oaks, CA: Sage
  • 응답자의 응답값과 결측치 간에 강한 상관관계가 존재한다는 가정 하에, 관측치 간의 회귀 방정식을 통해 결측치를 예상한다.
  • 결측치를 포함하는 변수를 종속변수로, 나머지 변수를 독립변수로 하는 회귀식을 구성하고, 이렇게 추정된 회귀식의 예측값으로 결측 값을 대체한다.
  • 변수의 특성(연속형 변수, 범주형 변수, 교호작용항 및 이차항 등)에 따라 회귀식을 구성해 예측력을 향상한다.
  • 한계점
    • 회귀식을 이용한 단일 대체는 결측 값을 실제로 관찰된 값으로 가정하고 분석하므로, 결측값 대체의 불확실성을 고려하지 않는다.
    • 표준오차가 과소추정되어 p-value가 실제보다 작아지고, 신뢰구간이 좁아질 수 있음

 

 

 

4. 핫덱대체법(Hot deck)

  • 비슷한 성향을 가진 케이스의 값으로 결측 값을 대체한다.
  • 자료 분포를 가정하지 않으며, 다른 변수(나이, 성별, 소득 등)가 유사한 응답자의 값을 임의로 추출해 결측치를 대체한다.
  • 한계점
    1. 어떤 변수를 기준으로 대체군을 형성하는지와 어떤 순서로 기준을 적용하는지에 따라 결측값 대체 값이 달라진다.
    2. 대체에 대한 명확한 분포 가정이나 모형을 정의하지 않으므로, 수리적으로 편의(bias)를 계산할 수 없어 대체 결과를 평가하기 어렵다.

 

 

 

5. 가중치 보정법(Weighting)

  • 결측치를 대체하지 않고 관측된 값에 가중치를 부여하여 보정함.
  • Data를 특정 변수(성별, 연령대 등)에 따라 집단을 나누고, 각 집단의 조사설계 시 예상 관측 치수에 대비한 실제 응답한 관측치의 수를 비율로 응답 확률로 정의함
  • 응답 확률의 역수를 가중값으로 관측된 값에 곱해 분석 결과를 보정한다.
  • 한계점
    1. 가중값을 변수의 관측된 값에 곱하므로 해당 변수의 분산이 증가하게 된다.
    2. 무응답과 관련 있는 변수는 편의가 줄어드는 대신 분산이 늘어나는 상충관계지만, 무응답과 관련 없는 변수는 분산만 증가한다.

 

 

 

6. 기댓값 최대화 알고리즘(Expectaion-maximization algorithm, EM 알고리즘)

  • Little, R. J. & Rubin, D. B. (2002). Statistical Analysis with Missing Data. Hoboken, NJ: J Wiley & Sons.
  • 앞서 설명한 5가지 방법을 하나로 묶어 전통적 대체방법(Conventional imputation method)라고 하는데, 이러한 단일 대체 방법들은 각자 편향(bias), 분산 감소 등의 문제를 일으킬 수 있어, 실제 분석결과를 왜곡시킬 위험이 있다.
  • 이러한 문제를 감안하고도 완전한 데이터셋을 만들고자 한다면 EM 알고리즘을 사용하는 것이 유리하다.
  • E는 기댓값(Expectation), M은 최대화(Maximization)를 의미한다.
  • EM 알고리즘은 관측되지 않는 잠재변수에 의존하는 확률 모델에서 최대가능도(Maximum likelihood)나 최대 사후 확률(Maximum a posteriori)을 갖는 모수 추정 값을 E-step과 M-step 반복 계산을 통해 찾아내는 알고리즘이다.
  • E-step: 모수에 관한 추정 값으로 우도(Likelihood)의 기댓값을 계산하는 기댓값 단계
  • M-step: E-step의 기댓값을 최대화하는 모수 추정 값들을 구하는 최대화 단계
  • E-step과 M-step 과정을 반복하여 계산 결과가 충분히 수렴하게 되면, 최종 최대가능도나 최대 사후 확률을 통해 결측치의 대체 값을 결정한다.
  • 한계점
    1. EM 알고리즘 방식을 통한 단일 대체는 대체된 값의 분산이 너무 작다는 문제가 존재함

 

 

 

 지금까지 결측 값을 대체하는 방법 중 단일 대체 방법에 대해 학습해보았다. 단일 대체법은 간단하게 말하자면, 결측 값이 지나치게 많지 않은 상태(5% 이하이며, 그 이상은 보다 정교한 방식을 써야한다. 그러나 결측값이 30% 이상 넘어간다면, 정교한 방식을 사용하여도 제대로 추론하지 못한다.)에서 결측 값들을 채워서 완벽한 데이터 셋을 만드는 방법이다.

  • 결측값이 10% 미만이면 무슨 방법을 쓰던 큰 상관이 없으며, 그 이상부터는 회귀 대체를 포함한 보다 정교한 모델을 써야한다고 하지만, 개인적으로는 5% 미만을 생각하고 보다 보수적으로 접근하는 것이 안전하지 않나 싶다.
  • 결측값이 30% 이상 넘어간다면, 가능한 결측값 대체 방식을 사용하지 않기를 바란다(이전에 들었던 내용인데, 정확히 어디서 나온 내용인지 기억 나지 않아 확답은 힘들다만 결측값이 차지하는 비중이 지나치게 크다면, 관측값만으로 결측값을 추정하는데 한계가 생길 수 있으므로, 결측값 대체 방법에 너무 의존하지 않는 것이 좋다.)

 

 이 중 가장 쉽게 쓸 수 있으면서 효과도 썩 나쁘지 않은 방법은 완전 제거법인데, 결측 값을 감안하여 연구 대상인 표본 집단 영역을 보다 좁혀나가며 사용하기에 논리적으로도 큰 문제가 없는 방법이다.

 그러나 결측 값 대체를 해야한다면 일반적으로 회귀대체, EM알고리즘을 사용하며, 이 밖에도 k-nn 군집분석, 의사결정나무 등을 통해서도 결측값 대체를 할 수 있다.

 그러나 이러한 단일 대체 방법은 설정 방법이나 확률에 의존하는 경우 전혀 다른 대체 값이 출력될 위험이 존재하며, 범주형 데이터는 대체 값을 생성하였을 때, 정확도가 매우 낮다는 단점이 있다.

 다음 포스트에서는 이런 단일 대체 방법의 단점을 보완할 수 있는 방법인 다중 대체법(Multiple imputation)에 대해 알아보자.

728x90
반응형
728x90
반응형

결측 값(Missing value)

: 결측 값은 R에서는 NA, Python에서는 None, NaN으로 출력되며, 값이 기록되지 않았거나 관측되지 않은 경우, 데이터에 저장되는 값을 말한다.

 

 

 

결측 값이 분석 결과에 미치는 영향

1. 표본의 규모가 감소되어 검정력이 감소된다. 

SPSS, SAS, STATA 등과 같은 통계 분석 도구들은 결측 값을 자동으로 제외하는 방식을 기본값(Default)으로 적용하고 있으며, 하나의 변수를 기준으로는 결측 값의 비중이 크지 않더라도, 전체 데이터에서 하나라도 결측 값을 갖는 케이스를 모두 제외하면, 소실되는 표본 비율은 커지게 된다.

  • 표본이 10,000명인 Data가 있다고 할 때, 변수 A의 결측 값이 200명, 변수 B의 결측 값이 400명, 변수 C의 결측 값이 1,000명, 변수 D의 결측 값이 600명이라고 가정할 때, 최대 결측 값은 2,200명 최소 결측 값은 1,000명이다. 
  • Graham. J.W.(2012). Missing Data: Analysis and Edsign. New York : Springer. 참조

 

 

 

2. 표본의 대표성이 낮아져 분석 결과에 편향(Bias)을 가져온다.

무응답의 원인이 설문 문항에 대한 적대적 태도, 무관심, 이해 부족 등에서 비롯된 경우, 표본의 대표성에 치명적인 문제를 일으킬 수 있다.

  • 만약, 고등학교 당시 수학 성적을 물어보는 설문 문항이 존재한다면, 설문조사 대상자가 자신에게 악영향이 있을 것이라 생각하여 자신의 성적을 적지 않거나, 잘못된 성적을 적을 수 있다.
  • 위 경우, 평균이 85점으로 나왔으나, 실제로는 수학 성적이 낮은 사람들이 자신의 성적을 적지 않아 85점으로 나온 것이라, 실제 수학 점수 평균은 70점일 수 있다.
  • Stuart, E. A., Azur, M, Frangakis, C. & Leadf, P.(2009). “Multiple Imputation with Large Data Sets: A Case Study of the Children’s Mental Health Initiative, ”American Journal of Epidemiology, 169(9): 1133-1139. 참조

 

 

 

3. 결측으로 인한 실제 문제의 발생을 식별하기 어렵다.

결측 값은 항상 결과에 영향을 주는 것이 아니라, 영향을 줄 수도 있지만, 때로는 영향을 주지 않을 수도 있다.

  • 결측률이 5% 이하인 경우: 특별한 결측치 보정 없이 분석이 가능하다고 한다.
    • Graham, J. W. (2009). “Missing Data Analysis: Making it Work in the Real World,” Annual Review of Psychology, 60: 549-576
  • 무응답이 1% 이하인 경우 무시할 수 있으며, 1~5%인 경우 보정 처리가 가능하나, 15% 이상인 경우 정교한 보정이 필요하다고 한다.
    • Acuna & Rodriguez, C. (2004). “The Treatment of Missing Values and Its Effect in the Classifier Accuracy,” Classification, Clustering and Data Mining Applications. 639-647.
  • 설문의 종류, 길이, 구조 등에 의해 결측 값이 발생할 수 있다.
    1. 긴 설문조사는 응답자의 중도포기로 인해 상대적으로 뒤에 있는 문항에서 결측이 발생할 수 있다.
    2. 설문지 하단의 문항을 발견하지 못해, 무응답 처리될 수 있다.
    3. 흡연자를 대상으로 하는 설문 문항에서 비흡연자는 결측 값 처리가 되므로, 실제 결측 값이 존재하지 않는 문항이나, 표본 설정을 잘못하여 결측 값이 다량 발생한 것으로 판단될 수 있다.
    4. 위와 같은 문제는 결과에 유의미한 영향을 주지 않을 가능성이 높다.
  • 결측 값의 존재로 인해 발생하는 문제는 수집된 데이터를 통한, 사후 분석에 의해서만 알 수 있으므로, 숨겨진 문제(Hidden problem)이라고도 한다.

 

 

이번 포스트에서는 결측 값이 분석 결과에 미치는 영향에 대해 알아보았다. 다음 포스트에서는 결측 값의 종류에 대해 알아보도록 하겠다.

728x90
반응형

+ Recent posts