Sign In

[데이터 전처리] 결측치

Y
Yerim
  1. ML
  2. DataAnalysis

결측치

유효한 데이터 값이 존재하지 않는 누락 데이터를 NaN(Not a Number)으로 표시한다.
머신 러닝 분석 모형에 데이터를 입력하기 전 누락 데이터를 제거하거나 다른 값으로 대체하는 과정이 필요
누락 데이터가 많아지면 데이터의 품질이 떨어지고
머신러닝 알고리즘을 왜곡하는 현상 발생
원인
1.
데이터를 파일로 입력할 때 빠트리거나
2.
파일 형식을 변환하는 과정에서 데이터가 소실

결측치의 3종류

Missing Completely At Random (MCAR)

완전 무작위 결측
결측값의 발생이 다른 변수와 상관이 없는 경우
전산 오류, 통신 문제 등으로 데이터가 누락된 경우

Missing At Random (MAR)

무작위 결측
결측값의 발생이 특정 변수와 관련이 있으나 얻고자 하는 결과와는 상관이 없는 경우
어떤 상황으로 인해서 발생하는 경우, 기업에서 제조과정에서 A제조공정 B제조공정 중 A가 갑자기 문제가 발생해서 결측치가 발생 A공정에만 문제가 발생한 결측치

No Missing At Random (NMAR)

비무작위 결측
결측값 발생이 다른 변수와 상관이 있는 경우

결측치 확인

결측치 만들기

import pandas as pd
import numpy as np
from sklearn.datasets import fetch_california_housing

# 데이터셋 불러오기
california = fetch_california_housing()
X, y = california.data, california.target

# 결측치를 추가할 특성 선택
np.random.seed(42)
missing_percentage = 0.1  # 결측치 비율
n_missing_samples = int(missing_percentage * X.shape[0])
missing_samples = np.random.choice(X.shape[0], n_missing_samples, replace=False)
missing_features = np.random.choice(X.shape[1], n_missing_samples, replace=True)

# 결측치 추가
X_with_missing = X.copy()
X_with_missing[missing_samples, missing_features] = np.nan

# 데이터프레임 만들기
df = pd.DataFrame(X_with_missing, columns=california.feature_names)
df['target'] = y
df.head()

결측치 확인 - is.na()

DataFrame.isna()       # 결측치 True/False 반환
DataFrame.isna().sum() # 열별 결측치 개수
데이터 프레임 내에 결측 값을 확인하여 그 결과를 True / False로 반환
isna()를 따로 사용하기보다 sum()을 같이 사용하여 주로 결측치의 개수를 파악

결측치 시각화 - missingno 라이브러리**

count
import missingno as msno
import matplotlib.pyplot as plt
%matplotlib inline
msno.bar(df)
matrix
# matrix
import missingno as msno
import matplotlib.pyplot as plt
%matplotlib inline
msno.matrix(df)
heatmap
# heatmap
import missingno as msno
import matplotlib.pyplot as plt
%matplotlib inline
msno.heatmap(df)
dendrogram
# dendrogram
import missingno as msno
import matplotlib.pyplot as plt
%matplotlib inline
msno.dendrogram(df)

결측치 시각화 - seaborn

seaborn heatmap
import seaborn as sns

sns.heatmap(df.isnull(), cbar=False)

결측치 처리 방법

결측치 처리 방법: 아무것도 하지 않기, 데이터 제거, 데이터 대치

아무것도 하지 않기

일부 알고리즘은 결측치를 고려하여 학습한다. (XGBoost)
결측치를 무시하거나 대체하는 파라미터를 가지고 있는 모델도 있다.

데이터 제거 delete

행이나 열 전체를 제거하는 방법
결측치의 삭제에는 많은 리스크가 따른다.
데이터의 삭제는 데이터의 누락으로 이어질 수 있기 때문에 꼭 신중하게 진행해야 한다.
제거 기준 사람마다 다르기 때문에 도메인 지식을 바탕으로 적절히 평가해야한다.
가이드라인:
10% 미만: 삭제 or 대치
10% ~ 50%: regression or model based imputation
50% 이상: 해당 변수 자체 제거
DataFrame.dropna(*, axis=0, how=_NoDefault.no_default, 
    thresh=_NoDefault.no_default, subset=None, inplace=False, ignore_index=False)
# 결측치가 존재하는 행을 모두 제거
df.dropna(axis=0, how='any') # index 20640 rows -> 18576 rows

# 결측치가 존재하는 열을 모두 제거
df.dropna(axis=1, how='any') # column은 target만 남음
결측치를 삭제 할 때 사용한다.
axis: 0(index), 1(columns), default 0
how: any, all, default 'any'
any: NaN이 있을 때
all: 모두 NaN일 때
inplace: False, True

데이터 대치 imputation

데이터의 결측치를 다양한 값으로 대치할 수 있다.
평균 mean
중간값 median
최빈값 mode
특정 값
MICE
결측치를 확인하는 과정에서 데이터의 도메인 지식이 필요하다.
결측치가 유의미한 데이터일 수도 있기 때문에 도메인 지식을 잘 파악하는 것이 중요
평균 mean, 중앙값 median으로 대치
다른 feature 간의 상관관계 고려 X
절대 범주형 데이터엔 사용 X
이상치의 영향을 받는다.
최빈값 mode, 0, 상수값으로 대체
범주형 데이터에도 사용 가능
다른 feature 간의 상과관계 고려 X
상수값에 따라 이상치가 될 수 있다.
보간법
누락값 사이의 값을 평균으로 대체해서 만든다.
중간에 결측치가 있으면 보간법을 이용하여 결측치를 대체한다.
선형 보간법
두 점사의 거리 구하는 공식
1차원 두 점사이의 거리로 해당 누락값을 대체한다.
spline 보간법
기존에는 1차원으로 생각해서 보간
spline 통해 order 차수 높이면 2차원 이상으로
KNN 대체
K-Nearest Neighbor 알고리즘을 사용해 가장 근접한 데이터를 k개 찾는 방식
평균, 중앙값보다 정확한 경우가 있다.
이상치에 민감하다.
MICE
누락된 데이터를 여러번 채우는 방식으로 여러 결측치 대치 세트를 만들어 with 함수로 특정 통계모델링을 수행
pool 함수로 생성한 m개의 대치 세트를 평균하여 결과를 도출
연속형, 범위형 패턴도 처리 가능
imputation: distribution을 토대로 m개의 데이터셋을 imputation
analysis: m개의 완성된 데이터셋을 분석
pooling: 평균, 분산, 신뢰 구간을 계산하여 결과를 종합
딥러닝 이용
범주형이나 숫자가 아닌 자료형에 효과적
DNN을 이용해 누락된 값을 유추

결측치 대치의 다양한 방법들

DataFrame.fillna(value=None, *, method=None, axis=None, 
                                    inplace=False, limit=None, downcast=_NoDefault.no_default)
method: backfill, bfill, ffill, None, dafult None
backfill: backfill은 bfill과 완전히 동일한 기능을 수
bfill: 누락값이 나타나나 이후의 첫 번째 값으로 앞쪽의 누락값으로 변경
ffill: 누락값이 나타나기 전의 값으로 누락값을 변경
axis: 0(index), 1(columns)
inplace: False, True
원본 데이터
df['Latitude'].plot()
median
df['Latitude'].fillna(df['Latitude'].median()).plot()
mean
df['Latitude'].fillna(df['Latitude'].mean()).plot()
ffill
df['Latitude'].fillna(method='ffill').plot()
bfill
df['Latitude'].fillna(method='bfill').plot()
DataFrame.interpolate(method='linear', , axis=0, limit=None, 
                inplace=False, limit_direction=None, limit_area=None, 
                downcast=_NoDefault.no_default, *kwargs)
method: default linear
['linear', 'time', 'index', 'values', 'nearest', 'zero', 'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh', 'spline', 'polynomial', 'from_derivatives', 'piecewise_polynomial', 'pchip', 'akima', 'cubicspline']
linear: 값들을 동일한 간격으로 취급
time: 일일 및 더 높은 해상도 데이터에서 길이의 간격 보간
index: 인덱스의 실제 숫자 값 사용
values: 인덱스의 실제 숫자 값 사용
pad: NaN을 기존 값으로 채운다.
nearest, zero, slinear, quadratic, cubic, barcentric, polynomial -> scipy.interpolate.interp1d에 전달되는 메서드 (spline은 scipy.interpolate.UnivariateSpline에 저장
krogh, piecewise_polynomial, spline, pchip, akima, cubicspline: scipy 보간 방법의 wrapper
axis: 0(index), 1(columns)
limit
inplace: False, True
limit_direction: forward, backward, both (optional)
limi_area: None, inside, outisde
linear
df['Latitude'].interpolate(method='linear').plot()
slinear
df['Latitude'].interpolate(method='slinear').plot()
cubic
df['Latitude'].interpolate(method='cubic').plot()
spline
df['Latitude'].interpolate(method='spline', order=2).plot()
sklearn.impute.SimpleImputer(*, missing_values=nan, strategy='mean', 
        fill_value=None, copy=True, add_indicator=False, keep_empty_features=False)
parameters
missing_values
int, float, str,
np.nan(default),
None or pandas.NA
결측값을 나타내는 데이터 타입 또는 값
strategy
str, default=’mean’
’mean’: 평균
‘median’: 중앙값
‘most_frequent’: 최빈값
‘constant’: 고정값
결측값 대체 방법 선택
fill_value
str or numerical value,
default = None
strategy가 constant일 때 채울 값
copy
bool, default=True
입력 데이터(X)의 복사본 생성 여부
add_indicator
bool, default=False
대체된 결측값을 나타내는 지표 추가 여부
keep_empty_features
bool, default=False
입력 데이터에서 결측값이 없는 열 유지 여부
attributes
indicator
MissingIndicator
결측값의 위치를 나타내는 지표에 대한 정보
n_features_in
int
입력된 특성(열)의 개수
feature_names_in_
ndarray of shape (n_features_in_,)
입력된 특성(열)의 이름 배열
mean
imputer_mean = SimpleImputer(strategy='mean')
df_imputed = imputer_mean.fit_transform(df)
df_imputed = pd.DataFrame(df_imputed, columns=df.columns)

df_imputed['Latitude'].plot()
median
imputer_mean = SimpleImputer(strategy='median')
df_imputed = imputer_mean.fit_transform(df)
df_imputed = pd.DataFrame(df_imputed, columns=df.columns)

df_imputed['Latitude'].plot()
most_frequent
imputer_mean = SimpleImputer(strategy='most_frequent')
df_imputed = imputer_mean.fit_transform(df)
df_imputed = pd.DataFrame(df_imputed, columns=df.columns)

df_imputed['Latitude'].plot()
sklearn.impute.IterativeImputer(estimator=None, *,
missing_values=nan, sample_posterior=False, max_iter=10, tol=0.001,
n_nearest_features=None, initial_strategy='mean', fill_value=None,
imputation_order='ascending', skip_complete=False, min_value=-inf, max_value=inf,
verbose=0, random_state=None, add_indicator=False, keep_empty_features=False)
mice(data, m = 5, method = NULL, predictorMatrix, where = NULL, blocks,
  visitSequence = NULL, formulas, blots = NULL, post = NULL,
  defaultMethod = c("pmm", "logreg", "polyreg", "polr"), maxit = 5,
  printFlag = TRUE, seed = NA, data.init = NULL, ...)
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer

imputer_mice = IterativeImputer(random_state=111)
df_imputed = imputer_mice.fit_transform(df)
df_imputed = pd.DataFrame(df_imputed, columns=df.columns)

df_imputed['Latitude'].plot()
from impyute.imputation.cs import mice

np_imputed=mice(df.values)
df_imputed = pd.DataFrame(np_imputed, columns=df.columns)
df_imputed['Latitude'].plot()
impute.KNNImputer(*, missing_values=nan, n_neighbors=5, weights='uniform', 
                metric='nan_euclidean', copy=True, add_indicator=False, 
                keep_empty_features=False)[source]

결측치 보간 주의점

도메인 지식 없이 수치로만 다루면 한 쪽으로 치우쳐지는 경향이 있다
기존의 데이터의 분포와 데이터의 관계를 잘 알고 해당 특성을 반영하여 보간해야한다
Yerim-DevNote
Subscribe to 'Yerim-DevNote'
Subscribe to my site to be the first to receive notifications and emails about the latest updates, including new posts.
Join Slashpage and subscribe to 'Yerim-DevNote'!
Subscribe
👍