R NA 제거 - R NA jegeo

R에서 결측치(missing data)는 'NA'로 표현됩니다. 자동기기로 관측하지 않는 이상 결측치는 항상 존재할 수 밖에 없습니다. 결측치를 처리하는 방법은 두 가지로 요약될 수 있는 데 첫번째는 결측치를 포함되는 행을 제거하는 것이고 다른 하나는 평균이나 중앙값 등으로 대체(Imputation)하는 것입니다. 데이터 특성에 따라 취하는 방법이 다르기 때문에 어느 것이 더 좋다고 할 순 없습니다. 이번 포스팅에서는 R에서 결측치를 처리하는 방법을 알아보겠습니다.

2. 결측치 확인하기

행수가 많은 데이터에서 결측치를 일일이 확인하는 것은 굉장히 비효율적인 일 것입니다. 그래서 결측치를 확인하는 is.na() 함수를 사용하는 것이 효율적이고 사람들이 많이 사용하는 방법입니다. is.na() 함수는 거의 모든 자료형에 사용할 수 있으며 NA이면 TRUE, NA가 아니면 FALSE로 표현됩니다.

1. 데이터 결측치 찾기

* 샘플데이터 만들기

df <- data.frame(sex = c("M", "F", NA, "M", "F"),
                 score = c(5,4,3,4,NA));

is.na(df); //결측치 확인
table(is.na(df)); //결측치 빈도 출력

R NA 제거 - R NA jegeo

table(is.na(df$sex)) //성별 컬럼만 빈도 출력
mean(df$score) //점수의 평균을 구하려고하면 NA라는 데이터가 있기떄문에 결과값은 NA가 나온다.

2. 결측치 제거하기 filter() 이용

* is.na()를 filter()에 적용하면 결측치가 있는 행을 제거할 수 있다. 먼저 결측치가 있는 행만 추출한 다음 제거하기!

df %>% filter(is.na(score))
df <- df %>% filter(!is.na(score)) // is.na()앞에 '아니다'를 의미하는 ! 기호를 붙여 !is.na()를 입력하면 NA가 아닌 값을 출력할 수 있다.

df %>%  filter(is.na(sex))
df <- df %>%  filter(!is.na(sex))

3. 결측치가 하나라도 있으면 바로 제거할 수 있는 함수 na.omit()

* na.omit()은 결측치가 하나라도 있으면 모두 제거하기 때문에 간편한 측면이 있지만, 분석에 필요한 행까지 제거할 수 있다는 단점이 있다.

df_nomiss <- na.omit(df);

R NA 제거 - R NA jegeo

4. 결측치 제외기능 이용하기

mean(df$score, na.rm = T) //결측치 제외하고 평균 산출
sum(df$score, na.rm = T) //결측치 제외하고 합계 산출

R NA 제거 - R NA jegeo

Q. csv파일을 이용하여 na.rm을 사용하기

exam <- read.csv("text_csv.csv"); //데이터 불러오기
exam[c(3, 8, 11), "math"] <- NA // 3, 8, 11행의 math에 NA할당하기

R NA 제거 - R NA jegeo

* 코드에서 [] 대괄호는 데이터의 위치를 지칭하는 역할을 합니다. 대괄호 안에서 쉼표 왼쪽은 '행위치', 오른쪽은 '열위치'를 의미합니다.

exam %>% summarise(mean_math = mean(math)) //평균산출해서 mean_math 변수에 넣기

위의 결과값은 결측치가 있기 때문에 NA로 나옴

//평균산출해서 mean_math 변수에 넣는데 na.rm을 이용해서 결측치 제외

exam %>% summarise(mean_math = mean(math, na.rm = T))
exam %>% summarise(mean_math = mean(math, na.rm = T),
                   sum_math = sum(math, na.rm = T),
                   median_math = median(math, na.rm = T)) 

5. 평균값으로 결측치 대체하기

exam$math <- ifelse(is.na(exam$math), mean(exam$math, na.rm = T), exam$math)

drv(구동방식) 별로 hwy(고속도로 연비) 평균이 어떻게 다른지 알아보려고 한다. 따라서 먼저 두 변수에 결측치가 있는지 확인해보고 있다면 결측치를 제거한 후, 어떤 구동방식의 hwy 평균이 높은지 알아보자.

 

mpg <- as.data.frame(ggplot2::mpg)
mpg[c(65, 124, 131, 153, 212), "hwy"] <- NA
table(is.na(mpg$drv))
table(is.na(mpg$hwy))

mpg %>% 
  filter(!is.na(hwy)) %>% 
  group_by(drv) %>% 
  summarise(mean_hwy = mean(hwy))

 

 

R NA 제거 - R NA jegeo
drv에는 결측값이 없어 hwy에 있는 결측값만 제거 후 분석 실행

 

 

 

 

데이터 분석 시에 결과 값에 영향을 미치게 되는 값이 결측치 외에 한 가지가 더 있다.

바로 정상 범주에서 크게 벗어난 값인 이상치(Outlier)이다. 

이상치 역시 분석 결과를 왜곡시키기 때문에 결측 처리 후에 제외하고 분석해야 좀 더 정확한 결과를 가져올 수 있다.

 

이상치의 종류에는 대표적으로 두 가지가 있는데, 아예 존재할 수 없는 값 혹은 극단적인 값이다. 

 

전자의 예로는 성별 변수에 3이라는 값이 들어간 것이며, 후자의 예로는 몸무게 변수에 200이라는 값이 들어간 것이다.

성별 변수에 3이라는 값이 들어간 것은 논리적으로 존재할 수 없으므로 바로 결측 처리를 하고, 몸무게 변수에 200이라는 값이 들어갔으면, 정상 범위의 기준을 정해서 결측 처리를 함으로써 해결할 수 있다.

 

 

그렇다면 극단치의 기준은 어떻게 정할까?

통계적으로는 상하위 0.3% 나 상자 그림 1.5 IQR을 벗어나면 극단치로 판단한다.

mpg 데이터로 예를 들어보자.

 

mpg <- as.data.frame(ggplot2::mpg)
boxplot(mpg$hwy) 
boxplot(mpg$hwy)$stats #상자그림 통계치 출력

 

 

R NA 제거 - R NA jegeo
[출처] 실무에서 써먹는 머신러닝 (with R)

 

 

위의 상자 그림에서, 상자 밖 가로선이 극단치의 경계(Q1, Q3 밖 1.5 IQR 내 최댓값)를 의미하며, 이 선 밖에 있는 동그라미 표식이 극단치(Q1, Q3 밖 1.5 IQR을 벗어난 값)인 것이다.

상자 그림의 통계치 결과를 확인하면, 12~37을 벗어난 값을 극단 치라고 설정할 수 있고, 이 값에 NA(결측치)를 할당하면 된다. 이 결측치를 제외하고 drv 별 hwy 평균값을 구하는 코드는 다음과 같다.

분석을 위한 데이터셋을 확보했다 하더라도 바로 분석을 할 수 없는 경우가 많습니다. 그 이유는 바로 결측값, 이상치, 오입력 등이 있습니다. 따라서 데이터 분석에 적합하도록 데이터셋을 정제해야 합니다.

바로 이것을 전처리(data preprocessing)라고 합니다. 

결측값(missing value)

결측값은 입력이 누락된 값을 의미합니다. 보통 NA로 출력됩니다. 결측값이 포함되면 산술 연산에 문제가 생깁니다. 대부분의 산술 연산 함수는 결측값을 제외할 수 있는 옵션을 제공합니다. 

밑의 데이터를 토대로 설명하겠습니다 .

R NA 제거 - R NA jegeo

1) 결측치 확인

summary(dataset$price)

- 출력값 -
   Min    1st Qu.    Median    Mean    3rd Qu.  Max.       NA's 
-457.200    4.425    5.400    8.752    6.300  675.000       30 

sum(dataset$price) 
# NA가 하나라도 담겨 있으면 결과값으로 [1] NA 를 반환한다. 

summary() : 다양한 결과 값 출력

2) 결측치 제거

① sum() 함수에서 제공되는 속성 이용

sum(dataset$price, na.rm = T) 
출력값 : [1] 2362.9

na.rm = T : NA값 제외

② 결측데이터 제거 함수 이용

price2 <- na.omit(dataset$price) 

sum(price2)
출력값 : [1] 2362.9

length(price2) 
출력값 : [1] 270

na.omit() : NA가 들어 있는 항복 삭제

-  NA 항목을 다 삭제 하기 때문에 NA가 담겨있는 같은 행의 다른 항목들의 데이터들도 삭제 될 수 있는 위험이 존재.
-  데이터가 적을수록 분석결과가 신뢰도가 예측도가 떨어짐. 

3) 결측치 대체 

- 결측치를 0으로 대체하기  

x <- dataset$price # price vector 생성

head(x)  
출력값 : [1] 5.1 4.2 4.7 3.5 5.0 5.4

dataset$price2 <- ifelse(is.na(x),0,x)
ifelse(!is.na(x),x,0)

sum(dataset$price3) 
출력값 : [1] 2625.4

dataset$price2 <- ifelse(isx.na(x),0,x) : true 일 때 0, false 일 때 x로 출력.  / $를 붙이고 컬럼 이름을 넣으면 추가 

R NA 제거 - R NA jegeo

NA -> 0 출력

4) 결측치를 평균으로 대체하기 

dataset$price3 <- ifelse(is.na(x),round(mean(x, na.rm = T),2),x) 

mean(dataset$price3)
출력값 : [1] 8.751333

sum(dataset$price3)  
출력값 : [1] 2625.4

is.na(x),round(mean(x,na.rm = T) , 2) ,x) : true 일 때 평균 : round(mean(x,na.rm = T) , 2) / false 일때 x로 출력.