결측치의 존재를 확인하려면 complete.cases( )를 사용한다. complete.cases( )는 데이터 프레임의 각 행에 적용되며, 각 행에 저장된 모든 값이 NA가 아닐 때만 TRUE를 반환하므로 NA 값이 하나라도 존재하는 행을 찾는 데 편리하다. 다음은 아이리스에 일부러 NA 값을 입력하고 해당 행들을 다시 찾아내는 예다.
> iris_na <- iris > iris_na[c(10, 20, 25, 40, 32), 3] <- NA > iris_na[c(33, 100, 123), 1] <- NA > iris_na[!complete.cases(iris_na), ] Sepal.Length Sepal.Width Petal.Length Petal.Width Species 10 4.9 3.1 NA 0.1 setosa 20 5.1 3.8 NA 0.3 setosa 25 4.8 3.4 NA 0.2 setosa 32 5.4 3.4 NA 0.4 setosa 33 NA 4.1 1.5 0.1 setosa 40 5.1 3.4 NA 0.2 setosa 100 NA 2.8 4.1 1.3 versicolor 123 NA 2.8 6.7 2.0 virginica
만약 한 컬럼에 대해서만 NA 여부를 조사하고자 한다면 is.na( )를 사용한다.
> iris_na[is.na(iris_na$Sepal.Length), ]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
33 NA 4.1 1.5 0.1 setosa
100 NA 2.8 4.1 1.3 versicolor
123 NA 2.8 6.7 2.0 virginica
NA 값의 처리를 분류 알고리즘에서 지원하지 않는다면 해당 데이터를 제외하고 모델링 및 예측을 수행하거나, NA 값을 다른 값으로 대체해야 한다. 값을 대체하는 가장 간단한 방법은 데이터의 평균이나 중앙값을 취하는 것이다.
다음 코드는 iris_na에서 중앙값을 구한 예다. median( ) 호출 시 na.rm=TRUE는 NA를 제외하고 중앙값을 계산하기 위해 사용했다. 만약 na.rm=TRUE를 지정하지 않으면, NA 값이 포함된 연산의 결과는 NA므로 제대로 된 중앙값을 얻을 수 없게 된다. na.rm=TRUE는 mapply( )의 인자로 전달하지만 mapply( )에서 직접 사용되는 것은 아니고, median( )을 호출할 때 인자로 전달된다. mapply( )는 앞서 4.4.5절에서 살펴봤다.
> mapply(median, iris_na[1:4], na.rm=TRUE)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.8 3.0 4.4 1.3
이제 이 값들을 iris_na에서 NA 값이 위치한 곳에 대체하면 된다. DMwR에는 이런 작업을 좀 더 편리하게 해주는 centralImputation( ) 함수가 있다.
> install.packages(DMwR) > library(DMwR) > iris_na[!complete.cases(iris_na), ] Sepal.Length Sepal.Width Petal.Length Petal.Width Species 10 4.9 3.1 NA 0.1 setosa 20 5.1 3.8 NA 0.3 setosa 25 4.8 3.4 NA 0.2 setosa 32 5.4 3.4 NA 0.4 setosa 33 NA 4.1 1.5 0.1 setosa 40 5.1 3.4 NA 0.2 setosa 100 NA 2.8 4.1 1.3 versicolor 123 NA 2.8 6.7 2.0 virginica > centralImputation(iris_na[1:4])[ + c(10, 20, 25, 32, 33, 40, 100, 123), ] Sepal.Length Sepal.Width Petal.Length Petal.Width 10 4.9 3.1 4.4 0.1 20 5.1 3.8 4.4 0.3 25 4.8 3.4 4.4 0.2 32 5.4 3.4 4.4 0.4 33 5.8 4.1 1.5 0.1 40 5.1 3.4 4.4 0.2 100 5.8 2.8 4.1 1.3 123 5.8 2.8 6.7 2.0
단순한 중앙값 대신 모델을 만들어 NA를 대체할 값을 구할 수도 있다. 다음은 NA 값을 k 최근 이웃 분류 알고리즘을 사용해 k개 근접 이웃 값의 가중 평균으로 대체한 예다. 가중치는 NA 값이 있는 데이터와의 거리로부터 계산되며, knnImputation( )이 자동으로 데이터 정규화를 수행하므로 scale( )을 별도로 수행하지는 않았다.
> knnImputation(iris_na[1:4])[c(10, 20, 25, 32, 33, 40, 100, 123), ]
Sepal.Length Sepal.Width Petal.Length Petal.Width
10 4.900000 3.1 1.452250 0.1
20 5.100000 3.8 1.539881 0.3
25 4.800000 3.4 1.457144 0.2
32 5.400000 3.4 1.483821 0.4
33 5.462532 4.1 1.500000 0.1
40 5.100000 3.4 1.475718 0.2
100 5.891169 2.8 4.100000 1.3
123 7.077197 2.8 6.700000 2.0
위에 보인 방법들에서 NA를 대체할 값을 구할 때 Species는 사용하지 않았다는 점에 주목하기 바란다. 예를 들어, Sepal.Length를 중앙값으로 대체할 때 단순히 Sepal.Length 전체의 중앙값을 사용했으며, 붓꽃 종별로 중앙값을 계산해 사용하지 않았다. 만약 각 행의 Species 값을 참고해 Species.Length의 NA를 해당 붓꽃 종의 중앙값으로 대체한다면 NA가 있던 자리에 붓꽃 종별에 대한 직접적인 정보가 흘러들어갈 수 있다. 그러면 예측 모델을 만들었을 때 Sepal.Length의 예측력이 실제보다 크게 나타날 수 있다.