R 결측치 제거 - R gyeolcheugchi jegeo

사용데이터 :

df <- data.frame(col1 = c(1:3, NA, 3),
                 col2 = c("this", NA,"is", "text", "is"), 
                 col3 = c(TRUE, FALSE, TRUE, TRUE, TRUE), 
                 col4 = c(2.5, 4.2, 3.2, NA, 3.2),
                 stringsAsFactors = FALSE)

R을 사용하여 데이터를 전처리하기 위해서는 먼저 전체적으로 데이터의 형태를 살펴봐야 하는데 이 후 해야할 작업이 의미없는 데이터를 제거하는 과정이다. 보통 의미없는 데이터라고 함은 형태적인 의미와 내용적인 의미의 두 가지를 모두 말한다. 형태적 의미가 없음은 보통 NULL값이나 결측치(Missing)를 의미한다. 내용적 의미는 해당 값이 지나치게 작은 값이나 지나치게 큰 값을 가져서 전반적 데이터의 왜곡을 가져오게 하는 이상치(abnormal)이거나 데이터 분석에 사용하지 않는 변량 등을 말한다. 형태적으로 무의미한 데이터는 기계적으로 제거가 가능하지만 내용적 무의미한 데이터는 데이터에 대한 이해가 선행되어야 한다.

이렇게 형태적이든 내용적이든 데이터 분석에 적절치 않은 데이터를 제거하는 방법에 대해 알아보자.

is.na()

데이터를 정제하기 위해서는 먼저 데이터에 결측치가 포함되어 있는지를 체크해야 한다. 이때 사용하는 함수가 is.na()이다.

is.na()는 매개변수로 전달될 데이터프레임이나 벡터에 NA값이 포함되어 있는지를 체크해서 포함되어 있다면 TRUE, 포함되어 있지 않다면 FALSE를 리턴해준다.

is.na(df)
##       col1  col2  col3  col4
## [1,] FALSE FALSE FALSE FALSE
## [2,] FALSE  TRUE FALSE FALSE
## [3,] FALSE FALSE FALSE FALSE
## [4,]  TRUE FALSE FALSE  TRUE
## [5,] FALSE FALSE FALSE FALSE
sum(is.na(df))
## [1] 3

na.omit()

is.na()의 결과로 df 데이터프레임에 총 3개의 NA가 포함되었다는 것을 알게 되었다. 그러면 이제 na.omit()를 사용하면 NA가 제거된 데이터프레임이 생성된다.

na.omit(df)
##   col1 col2 col3 col4
## 1    1 this TRUE  2.5
## 3    3   is TRUE  3.2
## 5    3   is TRUE  3.2

위의 결과에서도 보이듯이 df 데이터프레임에서 NA가 포함되어 있던 두 번째와 네 번쨰 행이 제거된 데이터프레임이 반환된다.

dplyr의 filter()와 is.na() 사용

na.omit()를 사용하여 간단히 NA가 제거된 데이터를 얻을 수 있다. 하지만 R의 사용자들이 데이터 전처리에서 많이 사용하는 tidyverse, dplyr를 사용하게 되면 na.omit()를 사용하기가 애매할 때가 있다. 이런 경우에 filter()is.na()를 사용하여 na를 제거한다. 다만 이 경우에는 na를 제거할 열마다 is.na()를 적용해주어야 한다는 단점이 있다.

is.na()는 매개변수로 지정되는 벡터에 na가 있다면 TRUE를 리턴한다. 따라서 is.na()가 FALSE 조건을 사용하여 필터링 하거나 is.na()!를 붙여서 na가 없는 열에서 리턴되는 FALSE를 TRUE로 바꾸어서 TRUE인 행만 필터링하는 방법을 사용할 수 있다.

df |> filter(!is.na(col1), is.na(col2) == FALSE)
##   col1 col2 col3 col4
## 1    1 this TRUE  2.5
## 2    3   is TRUE  3.2
## 3    3   is TRUE  3.2

중복된 행의 제거

데이터프레임에서 전체 열에 동일한 값을 가지는 열이 존재하는 경우 이를 제거해야 할지 그냥 사용해야할 지 결정해야 한다. 이 값이 내용적으로 의미가 있을수도 있고 의미가 없을 수도 있기 때문에 이는 데이터를 잘 아는 전문가의 의견들 들어야 할 것이다. 만약 중복된 데이터가 내용적으로 의미가 없다면 이를 제거해야 한다.

중복된 데이터를 제거하는 방법은 distinct()를 사용한다. distinct()는 유일한 데이터 조합을 리턴하는 함수이기 때문에 이 함수를 사용하여 리턴된 데이터는 중복이 제거된 결과값이 된다. distinct()dplyr에서 제공하는 함수이기 때문에 %>%, |>와 같은 파이프를 사용한 데이터 전처리에 사용될 수 있다.

distinct(df)
##   col1 col2  col3 col4
## 1    1 this  TRUE  2.5
## 2    2 <NA> FALSE  4.2
## 3    3   is  TRUE  3.2
## 4   NA text  TRUE   NA

위의 결과에서 보면 df 데이터프레임은 총 5개의 행이지만 세 번째 행과 다섯 번째 행은 같은 값을 가지기 때문에 중복이 제거되어 총 4개의 행만 표시된다. 지금처럼 간단한 데이터프레임에서야 중복된 데이터를 간단히 찾을 수 있지만 대량의 데이터에서는 중복값을 제거하기란 쉽지않다.

행 인덱스나 row_number()를 이용한 행의 제거

만약 제거해야할 행의 번호를 알고 있을 때는 이 번호를 사용하여 행을 제거할 수 있다. 이 경우도 tidyversedplyr를 사용할 떄와 사용하지 않을 때로 나눌 수 있는데 dplyr를 사용한다면 row_number()를 사용하고 dplyr를 사용하지 않는 경우는 ’[]’를 사용한 인텍싱을 통해 제거할 수 있다.

na가 포함되어 있는 2, 4행을 제거하는 두가지 방법은 다음과 같다.

df[-c(2, 4), ]
##   col1 col2 col3 col4
## 1    1 this TRUE  2.5
## 3    3   is TRUE  3.2
## 5    3   is TRUE  3.2
df |> filter(!row_number() %in% c(2, 4))
##   col1 col2 col3 col4
## 1    1 this TRUE  2.5
## 2    3   is TRUE  3.2
## 3    3   is TRUE  3.2

조건을 이용한 행의 제거

데이터 전처리에서 na를 포함한 행을 제거하는 형식적 제거 외에 내용적인 행의 제거에는 사실 특정 조건을 사용하는 경우가 일반적이다. 이 경우도 dplyr를 사용하는 것이 편리하다.

dplyr를 사용할 때는 filter()를 사용하여 제거해야 할 조건을 명시하는데 조건에 걸리는 행이 남는다. 그러나 조건에 해당하는 행을 제거하기 위해서는 !를 사용하여 남겨질 조건에 대한 역을 사용한다.

df |> filter(!(col2 == FALSE))
##   col1 col2 col3 col4
## 1    1 this TRUE  2.5
## 2    3   is TRUE  3.2
## 3   NA text TRUE   NA
## 4    3   is TRUE  3.2

R - 통계 언어

R 결측치 찾기, 결측치 제거, 결측치 생성, 결측치 대체하기

티멀 2022. 6. 1. 14:43

결측치 찾기

데이터프레임내의 결측치는 NA로 배정된다.

데이터프레임을 만들 때 결측치를 넣으려면 NA를 입력하고 쌍따옴표를 생략한다.

df <- data.frame(gender = c(1, 2, 3, NA, 5),
                 score = c(5, 4, NA, 2, 1)
                 )

결측치를 찾으려면 table() 함수 내에 is.na()로 데이터프레임을 매개변수로 넣어 입력한다.

table(is.na(df))


#결과물
FALSE  TRUE 
    8     2

각각의 행열의 결측치 위치를 확인하려면 is.na(df)로 입력한다.

is.na(df)


#결과물
     gender score
[1,]  FALSE FALSE
[2,]  FALSE FALSE
[3,]  FALSE  TRUE
[4,]   TRUE FALSE
[5,]  FALSE FALSE

각 행의 결측치도 $표시를 활용해 개별 접근이 가능하다

table(is.na(df$gender))

FALSE  TRUE 
    4     1

결측치 제거하기

제거 시 dplyr 라이브러리에 있는 내장함수 filter를 사용한다.

#filter를 사용하고 !를 사용하여 결측치가 아닌 열만 남기기
df %>% filter(!is.na(score))

#연산자도 사용가능
df %>% filter(!is.na(score) & !is.na(gender))

일일이 다 해줄수도 있지만 한번에 제거해주기 위해서는 아래의 함수 사용

#만약 결측치 행이 100개가 있다면 너무 불편하므로 na.omit이란 함수를 대신 사용
#이 함수는 각각의 행에 하나라도 결측치가 있다면 그 행을 전부 제외해주고 출력해준다
#단 데이터손실이 많기 때문에 잘 쓰지는 않음
na.omit(df)

알아서 결측치를 제외해주고 연산을 해주는 na.rm 옵션 사용

#알아서 결측치를 제외하고 연산을 해주는 na.rm
mean(df$score, na.rm = T)
sum(df$score, na.rm = T)

결측치 생성

필요할 시 일일이 결측치를 넣어줄 수도 있다. 

d <- read.csv('csv_exam.csv')
#d 데이터에 maht행 3, 8, 15열에 결측치 추가
d[c(3, 8, 15), 'math'] <- NA

결측치 대체

결측치 있는 행을 다 제거해버리면 데이터의 손실이 많이 나므로 최빈값 혹은 평균값으로 결측치를 대체하여 넣는다.

혹은 머신러닝을 통해 이 특정값을 추청하는 모형을 만든 후에 그 예측된 값을 대신 넣기도 한다.

#평균으로 대체하기 위한 평균값
d_mean <- mean(d$math, na.rm = T)
#ifelse를 활용한 결측치를 평균값으로 대체
d$math <- ifelse(is.na(d$math), d_mean, d$math)