[ADP] R - 결측값 처리와 이상값 탐색

2020. 7. 14. 16:49ADP | ADsP with R/Knowledge

 R언어와 사용법에 관한 게시글들은 "한국 데이터 산업진흥원"에서 출간한 국가공인 ADP/ADsP자격증을 위한 "데이터 분석 전문 가이드"에 서술된 수준에 한정지어 다루어 볼 것입니다.

 ADP필기 준비를 위한 R의 기본적인 문법과 패키지들을 학습을 목표로 합니다.   


 

1. 데이터 탐색

 데이터를 본격적으로 분석하기 이전에는 데이터의 특서을 파악하고 데이터에 대한 통찰을 얻기 위해 다각도로 접근해야한다. 그 작업 중 하나가 바로 기초통계량(기술통계량)을 확인하는 작업이다. 

data(iris)         # 데이터 로드
head(iris)         # 데이터 생김새 파악
str(iris)          # 데이터 구조를 파악
summary(iris)      # 데이터의 평균과 4분위수 파악
cov(iris[,1:4])    # 데이터의 공분산 파악
cor(iris[,1:4])    # 데이터의 상관계수 파악

 데이터를 맨 처음 접할 때, 데이터의 기본적인 틀을 확인할 수 있어야한다. 특히 위와같이 가장 기본적인 통계치를 확인하여

1. 데이터의 분포가 어떤지

2. 데이터가 가진 변수간의 상관성이 존재하는 지(다중공선성)

3. 얼마나 상관성이 존재하는지(무시해도 되는 정도인지) 

알아볼 필요가 있다. 

2. 결측값 처리

 R에서는 결측값을 처리하는 다양한 방법이 있다. 기본적인 처리방법 / Amelia 2, Mice, mistools 패키지를 사용하여 결측값을 처리하는 방법 등 여러가지 있는데, 여기서는 기본적인 처리방법과 Amelia2의 처리방법을 확인해보도록 하겠다.

1) 기본 처리 방법

# 결측값 확인
y <- c(1,2,3,NA)
is.na(y)

# 특정 데이터 결측값으로 처리
mydata[mydata$v1 == 99, "v1"] <- NA

# 데이터 처리에서 결측값 문제 해결
x <- c(1,2,NA,3)
mean(x)                  ## 결과 NA
mean(x,na.rm=T)          ## 결과 2

# 결측값이 포함된 관측치 한번에 제거
mydata[!complete.cases(mydata),]

 결측값 확인은 is.na() 함수를 통해서 할 수 있다.

 그리고 특정 데이터가 "999"와 같은 일정 수치로 결측치가 되어있을 경우 boolean indexing을 통해서 한 번에 처리할 수 있다.

 또한 결측값이 존재하면 수치적 연산의 결과가 NA로 출력되기에 결측값제거 na.rm = T를 통해서 결측값을 연산시 제거할 수도 있다.

 그리고 complete.cases() 함수를 사용하여 한 번에 결측치를 제거할 수도 있으나 결측값이 넓게 분포된 경우 많은 데이터의 손실이 발생할 수 있으니 염두해 두어야 한다.

2) Amelia 패키지 사용

# 패키지 설치 및 로드
install.packages("Amelia")
library(Amelia)

# 데이터 로드 및 확인
data(freetrade)
head(freetrade)
str(freetrade)

 간단하게 데이터의 구조와 형태를 알아보았다. 

 대표적인 결측값 처리방법에는 해당 레코드 삭제가 있다. 하지만, 결측값이 많을 경우 해당 레코드를 모두 삭제하게 되면 데이터의 큰 손실이 발생하는 문제가 생긴다. 이러한 경우를 방지하기 위해 결측값을 대표값(평균/메디안 등)으로 대체하는 경우도 있다. 하지만 이 경우도 문제가 발생할 수 있다.

 이 대신에 변수들의 관계를 이용한 multiple imputation방법이 있다.

# amelia를 통한 imputation 데이터 셋 생성

a.out <- amelia(freetrade, m=5, ts = "year", cs = "country")

# m - 몇 개의 imputation 데이터 셋을 만들 것인가?
# ts - 시계열에 대한 정보
# cs - cross-sectional 분석 정보


hist(a.out$imputations[[3]]$tariff, col = "grey", border = "white")
save(a.out, file = "imputations.RData")

 위의 과정을 통해 a.out은 결측값들이 imputation 방법에 의해 대체된 5개의 데이터셋을 포함하고 각 데이터셋outdata1.csv / outdata2.csv / outdata3.csv / outdata4.csv / outdata5.csv으로 저장된다.  

아래  missmap을 통해 결측값의 비중을 확인해보자. 

# imputation방법으로 결측값을 처리하기 전의 결측값
missmap(a.out)

  

 이때는 5퍼센트의 결측값이 발생하였다. 

# imputation을 한 데이터셋의 결측값
freetrade$tariff <- a.out$imputation[[5]]$tariff
missmap(freetrade)

 결측값의 비중이 2퍼센트로 준 것을 확인할 수 있다. 완전하게 처리되지 않은 결측값은 또 같은/다른 방식으로 대체하여 결측값을 없애면 된다. 

 이러한 방식으로 multiple imputation을 활용하여 결측값 처리를 진행할 수 있다. 결측값을 처리하는 것은 데이터의 연산에 있어서 매우 중요한 부분이니 꼭 확인하고 처리할 필요가 있다.  

 

3. 이상값 탐색

 이상값은 데이터 분석에 있어서 전처리를 어떻게 진행할지와 부정사용방지 시스템에서 규칙을 발견하는데 사용할 수 있다. 

이상값은 

a1 - 의도하지 않게 잘못 입력한 경우

a2 - 의도하지 않게 입력됐으나 분석, 목적에 맞지않아 제거해야하는 경우

a3 - 의도되지 않은 현상이지만 분석에 포함해야하는 경우

b1 - 사기값(fraud) 

으로 나뉠 수 있다. 이 중 a1,a2는 bad data로 분류할 수 있지만, a3, b1은 이상값이라고 판단할 수 있다. 

 이를 판단하는 알고리즘으로 ESD, MADM 등이 있지만 이상값으로 부터 매우 민감하다는 단점이 있다. 그렇기에 데이터 산업 진흥원에서 저술한 가이드책에는 이상값을 처리하는데 너무 많은 시간을 낭비하지 말라고 한다. 하지만 꼭 해야하는 경우는 1. 불량데이터를 이상값으로 처리한 경우 / 2. 특정 변수가 trade-off의 판단이 필요할 때 의사결정 /과 같은 경우에는 이상값 처리가 필요하다고 한다. 이때 이상값을 처리하지 않으면 분석 데이터의 결과 자체가 모두 엉망이 될 가능성이 있다. 

 가장 쉽게 이상값을 확인하는 방법으로 boxplot이 있다. 

1) boxplot

# 정규분포 난수 100개 / 이상값이 없는 경우
x = rnorm(100)
boxplot(x)

# 임의로 이상값을 입력 / 이상값이 발생하는 경우
x = c(x, 19, 28, 30)
outwith = boxplot(x)

# 이상값을 직접 출력
outwith$out

 위의 방식은 가장 기본적인 boxplot을 활용하여 이상값을 확인하는 작업을 진행했다. 위의 boxplot처럼 이상값이 존재하면 데이터의 외형적인 분포가 크게 달라짐을 확인할 수 있다. 

 또 다른 방법으로 outliers 패키지를 사용하여 이상값일 가능성이 큰 값을 찾아내는 방법이 있다. 이 outlier함수는 평균과 거리가 큰 값을 찾아내는 방법이다. 

2) outliers package

# outliers 패키지 설치 및 로드
install.packages("outliers")
library(outliers)
# 난수 생성
set.seed(1)
y=rnorm(100)
# 평균과 가장 차이가 많이 나는 값 출력
outlier(y)

# 반대 방향에서 평균과 가장 차이가 많이 나는 값 출력
outlier(y, opposite = TRUE)

 

# 20행 5열의 행렬 생성
dim(y)

# 각 열의 평균과 가장 차이가 많이 나는 값을 각 열별로 출력
outlier(y)

# 위의 결과와 반대방향으로 평균과 가장 차이가 많이나는 값을 각 열별로 출력
outlier(y, opposite = TRUE)

 outlier은 각 열별/column별 이상값들의 가능성이 높은 것을 찾아낼 수 있다. 하지만, 이는 데이터가 적을 수록 신중해야하며 이상값일 가능성이 높은 값인 것이지, 진짜 이상값이 아닐 수도 있다. 

 그렇기에 boxplot이 더 직관적으로 데이터의 이상값을 확인해주는 방법일 수도 있다. 

boxplot(y)