본문 바로가기
R

[preprocessing] 결측값(NA) 찾기 (colSums(is.na()), complete.cases(), filter(), filter_all(), select_if())

by phyzik 2023. 8. 25.

데이터를 다루다 보면 결측값 즉, 비어있는 값을 보게 됩니다. 대부분의 실제 현장에서 수집하는 데이터들은 완전히 수집이 되지 않아 결측값이 존재합니다. 결측값은 수집시 오류가 발생하거나, 기록시 누락 또는 미응답 등 수집 과정에서 문제가 생겨 데이터의 공백이 생긴 경우 입니다. 대부분 결측값을 "NA"로 표시가 되며 도메인에 따라 -999, -99 등으로 표기가 되기도 합니다. 그럼 R에서 결측값을 찾는 방법에 대해 몇 가지 알아 보도록 하겠습니다. 

먼저 R에서 결측값이 포함된 데이터를 불러 오겠습니다.결측값이 포함된 간단한 예제 데이터를 만들어서 사용해보도록 하겠습니다.

 library(tidyverse)
 > sample = tibble(var1 = c(1,10,100,NA,NA),
                  var2 = c(2, 20, 100,1000,10),
                  var3 = c(1, 60, 500,2000,60),
                  var4 = c("a",NA,NA,"d","c"))
 sample
# A tibble: 5 x 4
   var1  var2  var3 var4 
  <dbl> <dbl> <dbl> <chr>
1     1     2     1 a    
2    10    20    60 NA   
3   100   100   500 NA   
4    NA  1000  2000 d    
5    NA    10    60 c

 

1. colSums(is.na())

먼저, 결측값인지 아닌지를 TRUE, FALSE 논리값을 반환해주는 함수는 is.na()입니다. 해당 논리값을 사용하여 colSums() 함수를 사용하면 data.frame에서 모든 열의 결측값의 합을 알 수 있습니다. 

> colSums(is.na(sample))
var1 var2 var3 var4 
   2    0    0    2

 

2. complete.cases()

해당함수는 결측값인지 아닌지 TRUE, FALSE로 반환해주는 함수 입니다. 이름에서 알 수 있듯이 complete.cases즉, 완전전한 경우만 TRUE로 출력합니다. 즉, 결측값이 아니면 TRUE, 결측값이면 FALSE를 반환합니다.  

> complete.cases(sample)
[1]  TRUE FALSE FALSE FALSE FALSE

TRUE는 결측값이 아닌 값, FALSE는 결측값이 들어가 있는 row입니다. complete.cases()를 사용해서 결측값이 들어가 있거나 혹은 들어가 있지 않은 row만 출력할 수 있습니다. 

 

> sample[!complete.cases(sample),]
# A tibble: 4 x 4
   var1  var2  var3 var4 
  <dbl> <dbl> <dbl> <chr>
1    10    20    60 NA   
2   100   100   500 NA   
3    NA  1000  2000 d    
4    NA    10    60 c

위 코드와 같이 !complete.cases(sample)를 sample의 row값으로 넣어주면 됩니다. 여기서 "!"는 부정의 뜻입니다. 즉, 완전한 값이 아닌 결측값이 하나라도 들어어있는 모든 row를 출력하라는 의미 입니다. 

 

3. filter

dplyr의 filter함수를 사용해서도 결측값을 찾을 수 있습니다. 해당 방법은 특정 column의 이름을 넣어서 해당 column의 결측값을 모두 반환하는 방법입니다. var1의 결측값을 모두 반환해보겠습니다. 

> sample %>% filter(is.na(var1))
# A tibble: 2 x 4
   var1  var2  var3 var4 
  <dbl> <dbl> <dbl> <chr>
1    NA  1000  2000 d    
2    NA    10    60 c

var1에는 결측값이 총 2개가 들어가 있었습니다. var1의 결측값을 모두 반환하면서 그 결측값에 해당하는 다른 column들의 값도 반환하여 data.frame의 형태를 그대로 유지합니다. 

 

4. filter_all, select_if 

dplyr의 filter와 select는 그 용도가 다양합니다. 함수뒤에 _at, _all, _if를 붙여 다양하게 활용 할 수 있습니다. 해당 예제에서는 결측값을 찾는데 적용 해보도록 하겠습니다. 해다예제에서는 빠른 확인을 위해 간단한 예제 데이터를 만들어서 사용해보겠습니다.

 

먼저 select 입니다. select는 data.frame에서 원하는 column을 추출 할 때 사용하는 함수 입니다. 
select_if() 함수는 특정 조건을 만족하는 column을 추출 할 때 사용합니다. select_if()와 any(), is.na() 함수를 함께 사용하면 "NA"가 들어있는 모든 
column을 추출 할 수 있습니다. any()는 ()안의 조건중 하나라도 만족하는지 TRUE, FALSE로 반환해주는 함수 입니다. 

> sample %>% select_if(~any(is.na(.))) # NA들어간 컬럼 추출 
# A tibble: 5 x 2
   var1 var4 
  <dbl> <chr>
1     1 a    
2    10 NA   
3   100 NA   
4    NA d    
5    NA c

sample 데이터에서 NA가 포함된 모든 column을 추출 하였습니다. ~any(is.na(.))에서 any()앞의 "~"와 is.na()안의 "."은 purrr sytle의 함수형 프로그래밍 표시 입니다. "~"은 함수를 뜻하고, "."은 앞의 data.frame의 모든 column을 뜻합니다. 해당 내용과 관련하여서는 purrr 패키지에 대한 내용을 다룰 때 자세히 설명하도록 하겠습니다.

 

 

다음은 filter 입니다. filter는 조건에 맞는 row를 추출해주는 함수입니다. filter_all을 사용하면 모든 column에 대해 결측값이 하나라도 들어있으면 해당 row를 추출할 수 있습니다. filter_all()함수는 특히, any_vars(), all_vars()함수와 같이 사용됩니다. 

> sample %>% filter_all(any_vars(is.na(.)))
# A tibble: 4 x 4
   var1  var2  var3 var4 
  <dbl> <dbl> <dbl> <chr>
1    10    20    60 NA   
2   100   100   500 NA   
3    NA  1000  2000 d    
4    NA    10    60 c

코드를 해석하면, 모든 column에 대해 NA가 하나라도 들어가 있으면 해당 row를 추출하라는 함수 입니다. 

몇 가지 NA를 추출하는 방법에 대해 알아보았습니다. 여러가지 방법이 있겠지만 해당 방법을 사용하면 우선은 빠르게 결측값을 파악 할 수 있으리라 생각합니다. 감사합니다.