R

[R 기초] 데이터 전처리 - 결측값 처리 (삭제 & 대체)

Jin_Omics 2025. 2. 23. 13:42

데이터 분석에서 결측값(missing values)은 자주 마주치는 문제다.

결측값은 데이터에 값이 없는 경우를 의미하며, 데이터를 처리하는 과정에서 반드시 해결해야 하는 중요한 부분이다.

 

R에서는 결측값을 NA로 표시한다.

NA는 Not Available을 의미하며, R의 모든 기본 연산에서 특별한 값으로 취급된다.

결측값을 처리할 때는 NA를 다루는 방법을 알아야 한다.

 

이번 포스팅에서는 R에서 결측값을 처리하는 방법에 대해 다루며, 결측값을 삭제하거나 대체하는 기본적인 방법을 소개한다.


결측값 처리 방법 (삭제, 대체 등)

결측값을 처리하는 주요 방법은 두 가지:

  1. 결측값 삭제: 데이터셋에서 결측값을 포함하는 행(row) 또는 열(column)을 삭제하는 방법이다. 이 방법은 결측값이 적고, 삭제해도 데이터에 큰 영향을 주지 않을 때 유용하다.
  2. 결측값 대체: 결측값을 다른 값으로 대체하는 방법이다. 대체 방법에는 평균, 중앙값, 최빈값 등을 사용하거나, 더 복잡한 예측 기법을 활용할 수도 있다.

예제 데이터 

결측값을 대체할 예제 데이터를 소개한다.

예제 생성은 ChatGPT의 도움을 받았다.

 

이 데이터는 여러 변수들이 포함되어 있으며, NA (결측값)도 포함되어 있다.

# 예제 데이터 생성
data <- data.frame(
  id = 1:10,
  name = c("John", "Sara", "Tom", "Anna", "Mike", "Jill", "Sam", "Lily", "Jake", "Eva"), # 이름 변수(결측값 없음)
  age = c(25, 30, 22, 28, 35, 40, 60, NA, 55, 60),  # 나이 변수 (결측값 있음)
  gender = c("M", "F", "M", "M", "F", "F", "M", NA, NA, NA),  # 성별 변수(결측값 있음)
  score = c(90, NA, 80, 85, 88, 92, NA, 85, 78, 80),  # 성적 변수 (결측값 있음)
  salary = c(50000, 60000, 45000, 52000, 70000, 80000, 75000, 68000, 59000, NA)  # 연봉 (결측값 있음)
)

print(data)


결측값(NA) 처리: 삭제와 대체

1. 삭제

 

  • 결측값이 포함된 행(row)을 삭제: na.omit() 또는 complete.cases()
  • 결측값이 포함된 열(column)을 삭제: colSums()를 사용하여 결측값이 있는 열 삭제

 

 

(1) 결측값이 있는 행 삭제

  • na.omit(): 결측값을 포함한 행을 삭제 & 나머지 행들 반환
# 결측값 포함된 행 삭제
clean_data <- na.omit(data)
print(clean_data)

 

  • complete.cases(data): 각 행에 대해 결측값이 없으면 TRUE, 결측값이 있으면 FALSE를 반환하는 논리 벡터 생성
  • data[complete.cases(data), ]는 이 논리 벡터를 사용하여 결측값이 없는 행만 선택.

 

# complete.cases()를 이용하여 결측값이 없는 행만 선택
clean_data <- data[complete.cases(data), ]
print(clean_data)

 

 

💡 na.omit() 코드가 더 빠르고 간편, 모든 결측값을 일괄적으로 처리할 때 유용

💡 complete.cases()는 세밀한 제어가 필요할 때 유용 -> 특정 열에 대해서만 결측값이 없는 행을 필터링 가능

# 특정 열(age, gender)에서 결측값이 없는 행 선택 예시
clean_data <- data[complete.cases(data[, c("age", "gender")]), ]
print(clean_data)

 

 

(2) 결측값이 있는 열 삭제(간접적인 방법)

  • colSums(is.na(data))는 각 열에서 결측값이 몇 개 있는지 계산하는 함수
  • colSums(is.na(data))를 이용해 결측값이 있는 지 여부를 확인하고, == 0을 통해 결측값이 있는 열을 선택하고, 결측값이 없는 열만 새로운 데이터 프레임에 저장하는 방식으로 삭제한다
# 결측값이 있는 열 삭제
clean_data2 <- data[, colSums(is.na(data)) == 0]
print(clean_data2)
 
 

2. 대체

평균, 중앙값, 최빈값으로 대체하는 방법이 대표적이다.

 

1) 평균값(mean) 대체

# 나이(age) 변수의 결측값을 평균값으로 대체
data$age[is.na(data$age)] <- mean(data$age, na.rm = TRUE)
print(data)
  • mean(data$age, na.rm = TRUE)는 age의 NA를 제외한 평균을 계산,
    결측값(data$age[is.na(data$age)])을 평균으로 대체.
 

2) 중앙값(median) 대체

# 연봉(salary) 변수의 결측값을 중앙값으로 대체

data$salary[is.na(data$salary)] <- median(data$salary, na.rm = TRUE)
print(data)
  • median(data$salary, na.rm = TRUE)는  salary의 NA를 제외하고 중앙값을 계산,
    결측값(data$salary[is.na(data$salary)])을 중앙값으로 대체.

 

3) 최빈값(mode) 대체

R에는 최빈값을 계산하는 내장함수가 없기 때문에, 직접 'get_mode'라는 함수를 만들어 최빈값을 구했다.

후속 포스팅에 dplyr 패키지를 소개하면서 최빈값을 쉽게 구하는 방법을 다룰 예정이다.

(우선, R 기초 시리즈에서는 get_mode 함수를 직접 정의해서 해보겠다😅)

# `get_mode()` 함수 정의
# 이 함수는 주어진 데이터에서 가장 자주 등장하는 값, 즉 최빈값을 계산한다.
get_mode <- function(v) {
  uniqv <- unique(v)  # 고유값 구하기
  uniqv[which.max(tabulate(match(v, uniqv)))]  # 가장 빈도가 높은 값 반환
}

#최빈값 대체
mode_value <- get_mode(data$gender)  # 성별 변수의 최빈값 계산
data$gender[is.na(data$gender)] <- mode_value  # 결측값을 최빈값으로 대체

print(data)  # 결과 출력
  • (직접 정의한) get_mode() 함수는 가장 자주 나오는 값(최빈값)을 계산, 성별(gender)의 결측값을 최빈값으로 대체.


결측값 처리 방법 전체 적용 예시

# 결측값 대체: 나이(age) - 평균값, 성별(gender) - 최빈값, 점수(score) - 삭제 ,연봉(salary) - 중앙값
data$age[is.na(data$age)] <- mean(data$age, na.rm = TRUE)
mode_value <- get_mode(data$gender)
data$gender[is.na(data$gender)] <- mode_value
data <- data[!is.na(data$score), ]
data$salary[is.na(data$salary)] <- median(data$salary, na.rm = TRUE)

# 결측값 처리 후 데이터 확인
print("결측값 대체 후 데이터:")
print(data)

 


Summary

처리방법 함수 설명 예시코드
삭제 na.omit() 결측값이 포함된 '행(row)'  삭제 clean_data <- na.omit(data)
  complete.cases() 결측값이 포함되지 않은 행(row)'만 선택 clean_data <- data[complete.cases(data), ]
  colSums(is.na()) 결측값이 포함된 열의 개수를 계산해서, 결측값이 없는 열만 선택하는 간접적인 방법 data <- data[, colSums(is.na(data)) == 0]
대체 mean() 평균값으로 결측값 대체 data$salary[is.na(data$salary)] <- median(data$salary, na.rm = TRUE)
  median() 중앙값으로 결측값 대체 data$gender[is.na(data$gender)] <- get_mode(data$gender)
    최빈값 -- 함수 새로 정의해서 사용  

 

이번 포스팅에서는 기본적인 방법 만을 다뤘지만,  이 외에도 결측값을 대체할 때 예측 모델을 사용할 수 있다.

 

예를 들어, 회귀 분석이나 KNN(K-Nearest Neighbors) 등의 방법을 사용해 결측값을 예측할 수 있다.

  • 회귀 분석을 통한 대체
    • 결측값을 예측하는 데 사용할 수 있는 예측 모델을 만들고, 이를 기반으로 결측값을 대체할 수 있다.
  • KNN 대체 (K-Nearest Neighbors)
    • KNN 알고리즘을 사용하여 결측값을 가장 유사한 값들로 채울 수 있다.

이러한 방법들은 일반적인 결측값 처리 방법 외에도 보다 정교한 대체 방법으로 활용될 수 있다.

(추후 후속 포스팅 언젠가😵 예정)


🔥 TIP: 결측값 처리 시 자주 발생하는 실수와 예방 방법

  1. 결측값을 무작정 삭제하기: 데이터가 매우 중요하거나 결측값이 많은 경우, 삭제보다 대체가 나을 수 있다. (삭제는 분석에 큰 영향을 미칠 수 있기 때문)
  2. 잘못된 대체 방법 선택: 비정규 분포 데이터를 평균으로 대체하면, 결과가 왜곡될 수 있다. 중앙값 이나 최빈값을 사용하는 것이 더 적합할 수 있다.
  3. 결측값 처리 전처리가 부족한 경우: 결측값 처리는 분석의 시작이므로 반드시 데이터를 철저히 검토한 후 진행해야 한다.

이렇게 결측값을 처리하는 방법을 다뤘다.

결측값을 어떻게 처리하느냐에 따라 데이터 분석 결과가 달라질 수 있기 때문에 적절한 방법을 선택하는 것이 중요하다.