[Python] 데이터에서의 Missing Value 처리
우리가 흔히 분석하고 시각화, 더 나아가 모델링을 하는 과정까지 이어나가기 위해서는 앞단에서 데이터를 분석할 수 있는 형태로 잘 가공하고 전처리해야한다. 전처리해야하는 요소는 여러가지가 있지만 그 중에서 데이터프레임에 빈칸/None/NaN 으로 표시되는 결측값(Missing Value) 처리에 대해 알아보고자 한다.
실제 데이터는 우리 입맛에 맞게 원하는 변수(=칼럼), 결측치 없이 모든 값이 들어가 있는 행 등 이미 가공된 형태로 제공되지 않는다. 전처리되지 않은 데이터를 분석하거나 모델에 적용하게 되면 그 결과는 엉망이 되어버린다. 따라서 오늘 다루고자 하는 '결측값'은 꼭 적절한 방법으로의 처리가 필요하다.
0. 결측값의 종류
- MCAR : Missing Completely at Random (완전 무작위 결측) : 다른 변수들과 아무런 관련 없이 완전히 무작위로 결측값이 발생하는 경우
- MNAR : Missing at not Random (비무작위 결측) : 결측값이 다른 변수들과 관련이 있고 해당 변수들로 설명되지 않는 경우
- MAR : Missing at Random (무작위 결측) : 결측값이 다른 변수들과 관련이 있지만 해당 변수들로 설명되는 경우
1. 결측값 단순 제거
df 라는 변수명의 데이터프레임이 있다고 가정해보자.
# 모든 컬럼이 결측값인 행 제거(결측값 없는 컬럼이 있으면 제거가 안 된다.)
df_dropall = df.dropna(how='all')
# 두 개 이상의 컬럼이 결측값인 행 제거
df_droptwo= df.dropna(컬럼명 = 2)
# 특정 컬럼을 지정해서 결측값을 제거
df_컬렴명_drop = df.dropna(subset=['컬럼명'])
# 한 컬럼이라도 결측치가 있으면 행 제거
df_any_drop =df.dropna(how='any')
2. 결측값 단순 대체
결측값을 대체하는 방법은 정말 다양한데, 데이터의 분포나 도메인에 따라 다른 방법이 적용된다. 모델링을 위한 대체가 될 수도 있고, 기술적인 집계를 보기 위한 대체일 수도 있기 때문에 다양한 목적에 맞게 접근해야한다.
# 0으로 대체
df_0fill = df.fillna(0)
# 문자로로 대체 가능
df_hifill = df.fillna('안녕?')
# 평균, 분산, 표준편차, 최대, 최소, 중위 등등 다 가능
# 평균 대체
df_meanfill =df.fillna(df['칼럼명'].mean())
# 컬럼에 따라 대체하는 방법을 다르게 하고 싶은 경우
df_meanfill_sp=df.fillna({'칼럼명':df['칼럼명'].mean()})
3. 보간법
보간법은 양쪽 값들 사이에서 중간의 Missing Value를 주변의 값들을 통해서 유추하고 대체한다.
보간법에서 interpolate() 함수는 주어진 데이터 프레임 또는 시리즈의 결측값을 선형 또는 다항식 등의 방법을 사용하여 채우는 데 쓰이며, 여러 옵션을 제공하여 사용자가 원하는 보간 방법을 선택할 수 있다.
method 매개변수를 사용하여 선형 이외의 보간 방법을 지정할 수 있는데, 보간 방법에는 선형('linear'), 다항식('polynomial'), 시간('time'), 최근접 이웃('nearest') 등이 있다. 또한 interpolate() 함수는 데이터가 시계열 데이터인 경우 시간 간격을 고려하여 보간을 수행할 수도 있습니다.
#전 시점의 값들로 대체
df['칼럼명'].fillna(method='pad',inplace=True)
#뒤 시점의 값으로 대치하는 것
df.fillna(method='bfill')
#결측값을 여러 번 대치할지 아니면 한 번만 할지 방법을 조절할 수 있다.
#연속된 결측값이 있으면 한 번만 대치
df.fillna(method='bfill', limit= 1)
#수학적 방법이 아닌 특정 값으로 대치
df_sp_lin=df.interpolate(method='values')
4. Simple Imputer
보간법은 양쪽 값들 사이에서 중간의 Missing Value를 주변의 값들을 통해서 유추하고 대체한다.
보간법에서 interpolate() 함수는 주어진 데이터 프레임 또는 시리즈의 결측값을 선형 또는 다항식 등의 방법을 사용하여 채우는 데 쓰이며, 여러 옵션을 제공하여 사용자가 원하는 보간 방법을 선택할 수 있다.
method 매개변수를 사용하여 선형 이외의 보간 방법을 지정할 수 있는데, 보간 방법에는 선형('linear'), 다항식('polynomial'), 시간('time'), 최근접 이웃('nearest') 등이 있다. 또한 interpolate() 함수는 데이터가 시계열 데이터인 경우 시간 간격을 고려하여 보간을 수행할 수도 있습니다.
#단순하게 대체할 수 있는 패키지
from sklearn.impute import SimpleImputer
#constant로 지정해 상수로 대체하겠다는 뜻, 이외에도 다양한 strategy가 존재
constant_imputer =SimpleImputer(strategy='constant')
#모델에 fit시키기, 여러개 칼럼도 지정할 수 있다.
df_simple=constant_imputer.fit_transform(df['칼럼명'])
5. KNN Imputer
머신러닝 지도 학습의 KNN알고리즘을 활용해 거리 기반으로 결측치를 대체하는 방법이다.
from sklearn.impute import KNNImputer
df_knn = df.copy(deep=True)
#근처 3개 값을 기준으로 대치하고, 각 변수에 동일한 가중치 부여
knn_imputer = KNNImputer(n_neighbors=3, weights = 'uniform')
# 1차원 컬럼만 넣기 때문에 모델에 fit한 후 행,열 개념으로 바꿔서 배열로 변환
df_knn['칼럼명'] = knn_imputer.fit_transform(np.array(df_knn['칼럼명']).reshape(-1,1)) # 1차원 컬럼만 넣으니깐 행,열 transpoze 개념으로 바꿔서 형식 맞춘 것 배열로 형변환
KNNImputer의 하이퍼파라미터로 'weights'가 존재하는데 이에 대한 설명을 덧붙인다.
- weights = 'uniform' (균일한 가중치): 이 경우, KNN 알고리즘은 각 변수에 동일한 가중치를 부여한다. 즉, 결측값을 채우기 위해 이웃을 선택할 때 각 변수의 거리가 동등하게 고려된다.
- weights = 'distance' (거리 기반 가중치): 이 방법에서는 이웃을 선택할 때 변수 간의 거리에 따라 가중치를 부여한다. 예를 들어, 가까운 변수일수록 더 높은 가중치를 부여하고, 먼 변수일수록 낮은 가중치를 부여할 수 있다.
5. MICE 다중대치법
Round robin 방식을 반복하여 결측 값을 회귀하는 방식으로 결측치를 처리한다. 결측값을 회귀하는 방식으로 처리하기 때문에 이 방식은 수치형 변수에 자주 사용한다.
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
mice_imputer = IterativeImputer()
df_mice['칼럼명'] = mice_imputer.fit_transform(np.array(train_mice['칼럼명']).reshape(-1,1))
데이터에서 결측값을 처리하는 여러 방식을 살펴보았는데 주의할 점을 다시 한 번 강조하려 한다.
- 결측값을 확인하는 과정에서는 데이터의 도메인 지식이 필요하다. 결측값이 유의미한 데이터일 수도 있기 때문에 도메인 지식을 잘 파악하는 것이 중요하다.
- 결측치를 삭제하는 방법으로 처리를 하는 것은 많은 리스크가 따른다. 데이터의 삭제는 데이터의 누락으로 이어질 수 있기 때문에 꼭 신중하게 해야하고, 적절한 대치 방법을 탐색해야 한다.