데이터 전처리 (결측치, 이상치)

topics 100-데이터분석 & AI 103 데이터 분석,처리
types 실습 레퍼런스
tags

데이터 전처리 (결측치, 이상치)

왜 필요한가

데이터 전처리는 머신러닝 모델 학습 전에 데이터를 정제하고 준비하는 과정이다. Garbage In, Garbage Out - 더러운 데이터를 넣으면 결과도 나쁘다.

전처리를 하지 않으면?

  • 모델 학습 자체가 안 되거나 에러 발생
  • 편향된 결과가 나온다
  • 성능이 현저히 떨어진다
  • 통계 분석의 정확도가 떨어진다

핵심: 전처리가 잘 되어야 모델 성능도 좋다. 실제로 데이터 사이언티스트 업무의 80%가 전처리라고 한다.


결측치 처리

결측치란?

결측치(Missing Value)는 데이터에서 값이 없는 경우를 말한다. 머신러닝 모델은 대부분 결측치를 처리하지 못하기 때문에, 학습 전에 반드시 처리해야 한다.

결측치를 방치하면?

  • 모델 학습 자체가 안 되거나 에러 발생
  • 편향된 결과가 나올 수 있다
  • 통계 분석의 정확도가 떨어진다

처리 전략

  1. 제거: 결측치가 많은 행/열을 삭제
  2. 대체: 평균, 중앙값, 최빈값 등으로 채우기
  3. 예측: 다른 특성으로 결측치를 예측해서 채우기

언제 제거하고 언제 대체할까?

  • 결측치가 50% 이상이면 그 컬럼은 삭제하는 게 나음
  • 중요한 컬럼이면 대체나 예측 방법 사용
  • 랜덤하게 빠진 거면 대체, 패턴이 있으면 신중히 판단

1) 결측치 확인

# <span id="결측치가-몇개-존재하는지"></span>결측치가 몇개 존재하는지
df.isnull().sum()

# <span id="특정값이-몇개-존재하는지"></span>특정값이 몇개 존재하는지
(df['column명'] == '특정').sum()
df2 = df1.replace('_', None)

# <span id="특정열-value의-개수를-확인-시리즈를-반환"></span>특정열 value의 개수를 확인 (시리즈를 반환)
df["열이름"].value_counts()

# <span id="특정열-드롭"></span>특정열 드롭
df5 = df5.drop(columns=['new_date', 'opn_nfl_chg_date', 'cont_fns_pam_date'])

value_counts 옵션들

옵션 설명 기본값
normalize 비율로 반환할지 여부 False
sort 결과를 정렬할지 여부 True
ascending 오름차순 정렬 여부 False
dropna 결측치(NaN)를 제외할지 여부 True

출처: https://zzinnam.tistory.com/entry/pandas-valuecounts-%ED%95%A8%EC%88%98


2) 결측치 처리

결측치 제거

50%가 넘는 컬럼은 제거하는 게 일반적이다.

# <span id="결측치가-50-이상인-컬럼을-드롭"></span>결측치가 50% 이상인 컬럼을 드롭
threshold = len(df) * 0.5
df_cleaned = df.loc[:, df.isnull().sum() < threshold]

# <span id="같은-의미의-코드들"></span>같은 의미의 코드들
df.dropna(axis=1, thresh=int(len(df)*0.5)+1)
df.loc[:, df.isnull().mean() < 0.5]

왜 50%를 기준으로 할까?
데이터의 절반 이상이 없으면 해당 컬럼은 신뢰하기 어렵다. 대체해도 노이즈가 될 가능성이 크다.

결측치 대체 (Imputation)

# <span id="결측치를-중앙값으로-변환"></span>결측치를 중앙값으로 변환
df4 = df3.fillna({'age_itg_cd': df3['age_itg_cd'].median()})

# <span id="최빈값으로"></span>최빈값으로
df5 = df5.fillna({'cust_dtl_ctg_itg_cd': df5['cust_dtl_ctg_itg_cd'].mode()[0]})

언제 뭘 쓸까?

  • 평균(mean): 정규분포를 따르고 이상치가 없을 때
  • 중앙값(median): 이상치가 있을 때 (이상치에 덜 민감)
  • 최빈값(mode): 범주형 데이터일 때
  • 특정값: 도메인 지식이 있을 때 (예: 나이가 없으면 0)

이상치 처리

이상치란?

이상치(Outlier)는 다른 관측값들과 동떨어진 값을 말한다. 이상치가 있으면 모델이 왜곡되거나 성능이 떨어질 수 있다.

이상치를 방치하면?

  • 평균, 표준편차 등 통계량이 왜곡된다
  • 회귀 모델이 이상치에 끌려간다
  • 모델 성능 저하

하지만 항상 제거해야 하나?
아니다. 이상치가 의미 있는 데이터일 수도 있다. 예를 들어 사기 탐지에서는 이상치가 오히려 중요한 정보다.

처리 전 꼭 확인할 것: 해당 값이 데이터 오류인지, 의미 있는 데이터인지 맥락을 고려해야 한다


1) 이상치 탐지

시각화

boxplot이나 산점도 등을 이용하여 시각적으로 확인한다.

import matplotlib.pyplot as plt
import seaborn as sns

# <span id="산점도"></span>산점도
wine.plot.scatter(x='rat_ca', y='quality')
plt.scatter(wine['rat_ca'], wine['quality'])
sns.scatterplot(x="total_bill", y="tip", data=tips)

# <span id="박스플롯-그리기"></span>박스플롯 그리기
plt.boxplot([data_a, data_b, data_c])
sns.boxplot(x='group', y='value', data=df)

사분위(IQR) 범위

1사분면(25%), 3사분면(75%)을 이용한다.

$
IQR = Q3 - Q1
$

$
\text{이상치 기준:} \quad x < Q1 - 1.5 \times IQR \quad \text{또는} \quad x > Q3 + 1.5 \times IQR
$

import numpy as np
import pandas as pd

data = pd.DataFrame({'score': [10, 12, 13, 15, 18, 19, 100]})

Q1 = data['score'].quantile(0.25)
Q3 = data['score'].quantile(0.75)
IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# <span id="이상치-확인"></span>이상치 확인
outliers = data[(data['score'] < lower_bound) | (data['score'] > upper_bound)]

# <span id="이상치-제거"></span>이상치 제거
data_clean = data[(data['score'] >= lower_bound) & (data['score'] <= upper_bound)]

Z-score

평균과 표준편차를 이용한다. Z스코어 값의 절대값이 3 이상이면 보통 이상치로 판단한다.

$
Z = \frac{x - \mu}{\sigma}
$

from scipy.stats import zscore

data['z_score'] = zscore(data['score'])

# <span id="z-점수가-3을-초과하면-이상치로-간주-평균값으로-대체"></span>Z-점수가 3을 초과하면 이상치로 간주, 평균값으로 대체
mean_score = data['score'].mean()
data.loc[data['z_score'].abs() > 3, 'score'] = mean_score

2) 이상치 처리

결측치와 마찬가지로 이상치를 제거하거나 대체할 수 있다.

처리 방법

  1. 제거: IQR이나 Z-score 기준으로 이상치 행 삭제
  2. 대체: 평균, 중앙값으로 대체
  3. 변환: 로그 변환 등으로 범위 축소
  4. 유지: 이상치가 의미 있는 경우 그대로 사용

주의: 처리 전 해당 값이 데이터 오류인지 의미 있는 데이터인지 맥락을 고려해야 한다.


관련 문서