모델링하기 전에 결측치 조치는 필수
이다.
결측치 조치는 단변량분석을 진행하면서 실행
한다.
why?
결측치가 존재할 경우, 이상치 확인을 위한 boxplot과 이변량분석에서 가설검정도구들을 사용할 수 없기 때문
이다.
결측치는 df.info
또는 df.isna().sum()
(df.isnull().sum())으로 확인할 수 있다.
결측치를 조치하는 방법은 채우기 또는 제거
가 있다.
채우기에는 여러 가지 방법
이 있다.
제거
의 경우는 되도록 지양
하도록 한다.
why?
전처리 과정에서 결측치를 제거
한다는 것은 운영에서도 결측치가 들어올 경우
, 그 데이터를 버리겠다는 의미이다.
★Q. 모델링할 때는 결측치가 없던 변수여서 결측치 조치 코드가 없는 상황에 운영 시 얻은 데이터에는 결측치가 있다면 ?
A. 보통 결측치가 있던 변수에서 결측치가 발생한다. 만약 해당 경우가 발생한다면 이슈로써 접근한다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
titanic = pd.read_csv('titanic_train.csv')
# 확인
titanic.tail(2)
데이터 출처:https://www.kaggle.com/c/titanic
target = 'Survived'
x0 = titanic.drop(target, axis=1)
y0 = titanic[target]
# 필요한 라이브러리 불러오기
from sklearn.model_selection import train_test_split
# 필요한 라이브러리 불러오기
x, x_test, y, y_test = train_test_split(x0, y0, test_size=.1, random_state=2022)
x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=80, random_state=2022)
df.info
또는 df.isna().sum()
titanic.info()
titanic.isna().sum()
결측치 시각화를 하기 위해서 import missingno as msno
와 import matplotlib.pyplot as plt
를 import한다.
import missingno as msno
import matplotlib.pyplot as plt
msno.matrix(x_train)
plt.show()
msno.bar(x_train)
plt.show()
cf) https://scikit-learn.org/stable/modules/generated/sklearn.impute.SimpleImputer.html
strategy 속성에는
most_frequent
: 최빈값mean
: 평균값median
:중간값constant
: 지정한 값(숫자 또는 문자)를 사용할 수 있다.
constant를 사용할 경우, fill_value=숫자 / 문자
속성을 써줘야한다.
most_frequent 사용할 경우
- 보통 범주형(숫자는 이산형)을 채울 때
사용
from sklearn.impute import SimpleImputer
# 대상을 리스트로 선언
imputer1_list = ['Embarked']
# 선언하고 fit_transform
imputer1 = SimpleImputer(strategy = 'most_frequent')
x_train[imputer1_list] = imputer1.fit_transform(x_train[imputer1_list])
x_train.isna().sum()
constant를 사용할 경우
- fill_value=숫자 / 문자
속성을 추가한다.
x_train[x_train['Embarked'].isna()]
from sklearn.impute import SimpleImputer
# 대상을 리스트로 선언
imputer1_list = ['Embarked']
# 선언하고 fit_transform
imputer1 = SimpleImputer(strategy = 'constant', fill_value='S')
x_train[imputer1_list] = imputer1.fit_transform(x_train[imputer1_list])
x_train.isna().sum()
x_train[x_train['PassengerId'] == 62]
constant를 사용하지만 fill_value 속성을 추가하지 않은 경우
값이 제대로 들어가지 않는다.
from sklearn.impute import SimpleImputer
# 대상을 리스트로 선언
imputer1_list = ['Embarked']
# 선언하고 fit_transform
imputer1 = SimpleImputer(strategy = 'constant')
x_train[imputer1_list] = imputer1.fit_transform(x_train[imputer1_list])
x_train.isna().sum()
x_train[x_train['PassengerId'] == 62]
x_train['Age'].fillna(x_train['Age'].mean(), inplace=True)
x_train['Age'].isna().sum() # 0
x_train['Age'].replace(np.nan, x_train['Age'].mean(), inplace=True)
x_train['Age'].isna().sum() # 0
예측하는 방법은 정해져있지 않다.
선형회귀 알고리즘을 사용하여 채우는 사람도 본 적이 있다.
고유값을 나타내는 변수 제거와 가변수화가 선행
되어야 한다. Knn 알고리즘을 사용
하여 imputer
가 자동으로 결측치를 채워준다.스케일링이 선행
돼야한다.따라서 KnnImputer 내용은 뒤에서 다룬다.
air = pd.read_csv('https://bit.ly/3qmthqZ')
air.head(7)
데이터 출처: https://github.com/DA4BAM/dataset
air.isna().sum()
df.fillna(method='ffill')
또는 df.ffill()
tmp = air.copy()
tmp['Solar.R'].fillna(method='ffill', inplace=True)
tmp.head(7)
또는
tmp = air.copy()
tmp['Solar.R'].ffill(inplace=True)
tmp.head(7)
df.fillna(method='bfill')
과 df.fillna(method='backfill')
또는 df.bfill()
은 같은 기능을 한다.
tmp = air.copy()
tmp['Solar.R'].fillna(method='bfill', inplace=True)
tmp.head(7)
또는
tmp = air.copy()
tmp['Solar.R'].fillna(method='backfill', inplace=True)
tmp.head(7)
또는
tmp = air.copy()
tmp['Solar.R'].bfill(inplace=True)
tmp.head(7)
df.interpolate()
: 앞뒤의 값의 동일 간격으로 채움
tmp = air.copy()
tmp['Solar.R'].interpolate(inplace=True)
tmp.head(7)
범주형 변수의 경우, 보통 최빈값으로 결측치를 채운다.
x_train['Cabin'].fillna(x_train['Cabin'].mode()[0], inplace=True)
x_train['Cabin'].isna().sum() # 0
cf) Series.mode()는 결과가 Series이기때문에 Series.mode()[0]
을 써줘야 에러없이 최빈값을 가져와서 사용할 수 있다.
# Series.mode()
x_train['Cabin'].mode()
rate = 0.8 # 80% 아래면 삭제
int(len(x_train) * rate) # 712
# 기준 미달인 열 모두 삭제
filtered_x_train = titanic.dropna(thresh=int(len(x_train) * rate), axis=1)
msno.matrix(filtered_x_train)
plt.show()
결측치 조치를 채우기로 할 경우, 경우에 따라 왜곡이 크게 발생하는 경우
가 있을 수 있다.
이 경우, 채우기보단 제거하여 데이터의 정합성을 보존
할 수 있다.
x_train.dropna(inplace=True) # axis=0(행) 디폴트
x_train.isna().sum()
x_train.dropna(how='all', inplace=True)
x_train.isna().sum()
인터넷 등 찾아보면 thresh로 설정하는 값
에 대한 설명으로, "행이 해당 값 이상의 결측치를 갖고 있으면 삭제한다"고 되어있다.
그러나 해당 값은 행에 채워진 변수 개수(전체 변수 개수 - 행의 결측치 개수)
이다.
즉, 행에 결측치를 제외한 값이 채워진 변수들 개수
이다.
x_train.isna().sum()
len(x_train[(x_train['Age'].isna()) & (x_train['Cabin'].isna())])
len(x_train[x_train['Age'].isna()]) - len(x_train[(x_train['Age'].isna()) & (x_train['Cabin'].isna())])
최소 10개가 채워져 있지 않은 행들을 제거
x_train.dropna(thresh=10, inplace=True)
x_train.isna().sum()
x_train.dropna(thresh=12, inplace=True)
x_train.isna().sum()
print(len(x_train))
x_train
x_train.dropna(subset=['Age'], inplace=True)
x_train.isna().sum()