[R] selecting rows with NA

R 은 통계 분야에서 (특히 data science 쪽) 대세라고 할 수 있지요. 몇년 전부터 R을 익혀 데이터 분석에 이용하고 있는데, 많은 장점이 있으나 가끔 전혀 직관적이지 않는 결과를 내던지는 경우가 있어 멘붕했던 적이 여러번 있었습니다. 그 직관이란 것이 지극히 인간적인 것이고 사실은 R 이라는 프로그래밍 언어를 제대로 이해 못한 때문에 생기는 일들이지만, SAS나 Stata 와 같은 통계 패키지들을 다루면서는 겪지 않았던 괴상한 오류에 시달리는 것이 썩 기분 좋지는 않은 일이지요. 그 중 대표적인 것 중 하나가 NA 관련입니다. missing data 를 R 이 다루는 방법이 무척 엄격하달까요, 이걸 이해 못하면 정말 괴이한 에러에 머리를 쥐어 뜯게 됩니다.

다음과 같은 예를 보시지요. x2 컬럼에 NA 가 박혀 있습니다.

> df <- data.frame(no = 1:5, x1 = c(1, 0, 0, 1, 1), x2 = c(0, NA, NA, 1, 0))
> df
  no x1 x2
1  1  1  0
2  2  0 NA
3  3  0 NA
4  4  1  1
5  5  1  0
> df[df$x1 == 1, ]
  no x1 x2
1  1  1  0
4  4  1  1
5  5  1  0
> df[df$x1 != 1, ]
  no x1 x2
2  2  0 NA
3  3  0 NA

자, 여기까진 멀쩡하죠. 그런데 x2 에 조건을 줘서 선택을 하는 순간 괴상망칙한 현상이 벌어집니다.

> df[df$x2 == 1, ]
     no x1 x2
NA   NA NA NA
NA.1 NA NA NA
4     4  1  1
> df[df$x2 != 1, ]
     no x1 x2
1     1  1  0
NA   NA NA NA
NA.1 NA NA NA
5     5  1  0

이건 당연히 바라던 바가 아니었겠지요? 그냥 x2 가 1일 때, 또는 1이 아닐 때를 골라주면 될 걸, 왜 이런 어이없는 일이 벌어질까요?

> df$x2 == 1
[1] false    NA    NA  true false

이걸 보시면 이해가 되겠죠. NA 는 같은 지 다른지 비교가 안됩니다. 비교를 하려고 하면 결과는 그냥 NA. 그래서 행 전체가 NA 가 되버리는 참사가 벌어지죠.
이런 일이 안 벌어지려면? 이런 방법들이 있습니다.

> df[which(df$x2 == 1), ]
  no x1 x2
4  4  1  1
> df[which(df$x2 != 1), ]
  no x1 x2
1  1  1  0
5  5  1  0

만약 ‘x2 가 1 이 아닌 경우’에 NA 를 포함시켜 주고 싶다면, 번거롭지만 다음과 같이 해야 합니다.

> df[df$x2 != 1 | is.na(df$x2), ]
  no x1 x2
1  1  1  0
2  2  0 NA
3  3  0 NA
5  5  1  0

코드가 무척 지저분해 보이는데… 다른 해결 방법은? 물론 있죠. 그것도 아주 많이… R 인데!

## subset 함수 이용
> subset(df, x2 == 1)
  no x1 x2
4  4  1  1
> subset(df, x2 != 1)
  no x1 x2
1  1  1  0
5  5  1  0
> subset(df, x2 != 1 | is.na(x2))
  no x1 x2
1  1  1  0
2  2  0 NA
3  3  0 NA
5  5  1  0

## dplyr filter 이용
> library(dplyr)
> df %>% filter(x2 == 1)
  no x1 x2
1  4  1  1
> df %>% filter(x2 != 1)
  no x1 x2
1  1  1  0
2  5  1  0
 
> df %>% filter(x2 != 1 | is.na(x2))
  no x1 x2
1  1  1  0
2  2  0 NA
3  3  0 NA
4  5  1  0

## data.table 이용
> library(data.table)
> dt <- data.table(df)
> dt[x2 == 1]
   no x1 x2
1:  4  1  1
> dt[x2 != 1]
   no x1 x2
1:  1  1  0
2:  5  1  0
> dt[x2 != 1 | is.na(x2)]
   no x1 x2
1:  1  1  0
2:  2  0 NA
3:  3  0 NA
4:  5  1  0

다른 방법들이 그나마 덜 지저분해 보이지만, ‘1 아닌 것에 NA 도 포함되게’할 뭔가 더 깔끔한 방법은 찾지 못했습니다. NA 를 다루는데 is.na() 는 피할 수 없는 함수인가 봅니다. 피할 수 없으면 즐기랬나… ㅠㅠ

2018. 8. 20.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다