데이터를 수집하는 과정에서 누락되지 않도록하는 것이 가장 좋은 방법이지만, 이미 결측치가 존재한다면 이를 처리해 줘야합니다.
처리하는 방법으로는 크게 2가지가 존재하는데
DataFrame.isnull() 은 데이터마다 결측치 여부를 True, False로 반환합니다.
DataFrame.any(aaxis = 1) 은 행마다 하나라도 True가 있으면 True, 그렇지 않으면 False를 반환합니다.
두 개를 조합하여
DataFrame[DataFrame.isnull().any(axis = 1)] 를 하게 되면 값이 True인 데이터만 추출을 할 수 있게됩니다.
데이터가 범주형이더라도 수치형과 크게 다르지는 않습니다.
데이터를 수집하는 과정에서 중복된 데이터가 생길 수 있습니다.
같은 값을 가진 데이터 없이 행별로 값이 유일해야한다면 중복된 데이터를 제거해야합니다.
DataFrame.duplicated() 는 데이터 중복 여부를 불리언 값으로 반환해줍니다.
pandas에서 DataFrame.drop_duplicates를 통해 중복된 데이터를 손쉽게 삭제할 수 있습니다.
df = pd.DataFrame({'id':['001', '002', '003', '004', '002'],
'name':['Park Yun', 'Kim Sung', 'Park Jin', 'Lee Han', 'Kim Min']})
df
# id가 중복된 경우 나중에 들어온 값만 남기는 코드를 작성해보세요!
# ('Kim Sung' 항목이 삭제되어야 합니다.)
df.drop_duplicates(subset=['id'], keep='last', inplace=True)
df
이상치란 대부분 값의 범위에서 벗어나 극단적으로 크거나 작은 값을 의미합니다.
Min-Max Scaling 해보면 대부분의 값은 0에 가깝고 이상치만 1에 가까운 값을 가지게 될 것입니다.
이렇게 몇 개의 이상치 때문에 대부분 값의 차이는 의미가 거의 없어지게 됩니다.
극단적인 값이 생기는 경우를 제외하고 데이터를 고려하고 싶은 경우 이상치를 제거하고 분석합니다.
Z-score
- 가장 먼저 생각해 볼 수 있는 간단하고 자주 사용되는 방법입니다.
- 평균을 빼주고 표준편차로 나눈 후 특정 기준을 넘어서면 이상치라 판단하는 방법입니다.
- 기준을 작게하면 이상치라 판단하는 데이터가 많아지고
- 크게하면 이상치라 판단하는 데이터가 적어집니다.
# abs(df[col] - np.mean(df[col])) : 데이터에서 평균을 빼준 것에 절대값을 취합니다.
# abs(df[col] - np.mean(df[col]))/np.std(df[col]) : 위에서 얻은 값을 표준편차로 나눠줍니다.
# df[abs(df[col] - np.mean(df[col]))/np.std(df[col])>z].index: 값이 z보다 큰 데이터의 인덱스를 추출합니다.
def outlier(df, col, z):
return df[abs(df[col] - np.mean(df[col]))/np.std(df[col])>z].index
# Q. 무역수지 값을 기준으로 z=3일 때 이상치 데이터를 출력해보세요.
trade.loc[outlier(trade, '무역수지', 3)]
# Q. not_outlier() 함수를 구현하세요.
def not_outlier(df, col, z):
return df[abs(df[col] - np.mean(df[col]))/np.std(df[col])<z].index
IQR 방법
- IQR=Q3−Q1
- 즉, IQR은 제 3사분위수에서 제 1사분위 값을 뺀 값으로 데이터의 중간 50%의 범위라고 생각하시면 됩니다.
- 𝑄1−1.5∗𝐼𝑄𝑅Q1−1.5∗IQR 보다 왼쪽에 있거나,
- 𝑄3+1.5∗𝐼𝑄𝑅Q3+1.5∗IQR 보다 오른쪽에 있는 경우 우리는 이상치라고 판단합니다.
# 이상치가 포함된 데이터 생성
np.random.seed(2020)
data = np.random.randn(100) # 평균 0, 표준편차 1의 분포에서 100개의 숫자를 샘플링한 데이터 생성
data = np.concatenate((data, np.array([8, 10, -3, -5]))) # [8, 10, -3, -5])를 데이터 뒤에 추가함
data
# 박스플롯으로 이상치 확인하기
fig, ax = plt.subplots()
ax.boxplot(data)
plt.show()
# IQR을 먼저 구하기
Q3, Q1 = np.percentile(data, [75 ,25])
IQR = Q3 - Q1
IQR
# IQR을 통해 이상치 구하기
data[(Q1-1.5*IQR > data)|(Q3+1.5*IQR < data)]
# Q. 사분위 범위수를 이용해서 이상치를 찾는 outlier2() 함수를 구현해보세요.
def outlier2(df, col):
Q1, Q3 = np.percentile(df[col], [25, 75])
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df[col] < lower_bound) | (df[col] > upper_bound)]
return outliers
칼럼마다 스케일이 차이가 나게 되면 머신러닝 모델 학습에 문제가 발생할 수 있습니다.
예를 들어 키와 몸무게가 있다고 가정하면, 키의 범위가 몸무게의 범위보다 크기 때문에 데이터 간의 거리를 잴 때, 범위가 큰 키의 영향을 크게 받게 됩니다.


💡 train 데이터와 test 데이터가나눠져있는 경우, train 데이터를 정규화시켰던 기준 그대로 test 데이터도 정규화 시켜줘야합니다.
👏train 데이터에는 fit_transform / test 데이터에는 transform을 하는 이유입니다.👏
머신러닝이나 딥러닝 프레임워크에서 범주형을 지원하지 않는 경우 원-핫 인코딩을 해야 합니다.
원-핫 인코딩이란 카테고리별 이진 특성을 만들어 해당하는 특성만 1, 나머지는 0으로 만드는 방법입니다.
pandas에서 get_dummies 함수를 통해 손쉽게 구현할 수 있습니다.
#trade 데이터의 국가명 컬럼 원본
print(trade['국가명'].head())
# get_dummies를 통해 국가명 원-핫 인코딩
country = pd.get_dummies(trade['국가명'])
country.head()
# pd.concat 함수로 데이터프레임 trade와 country를 합쳐줍니다.
trade = pd.concat([trade, country], axis=1)
trade.head()
# 이제는 필요 없어진 국가명 컬럼을 삭제해 주고 나면 trade는 우리가 원하는 데이터프레임이 됩니다.
trade.drop(['국가명'], axis=1, inplace=True)
trade.head()
데이터를 구간별로 나누고자할 때 사용하는 기법으로 Data Binning, bucketing 이라 부릅니다.
히스토그램과 같이 연속적인 데이터를 구간을 나눠 분석할 때 사용하는 방법입니다.
salary = pd.Series([4300, 8370, 1750, 3830, 1840, 4220, 3020, 2290, 4740, 4600,
2860, 3400, 4800, 4470, 2440, 4530, 4850, 4850, 4760, 4500,
4640, 3000, 1880, 4880, 2240, 4750, 2750, 2810, 3100, 4290,
1540, 2870, 1780, 4670, 4150, 2010, 3580, 1610, 2930, 4300,
2740, 1680, 3490, 4350, 1680, 6420, 8740, 8980, 9080, 3990,
4960, 3700, 9600, 9330, 5600, 4100, 1770, 8280, 3120, 1950,
4210, 2020, 3820, 3170, 6330, 2570, 6940, 8610, 5060, 6370,
9080, 3760, 8060, 2500, 4660, 1770, 9220, 3380, 2490, 3450,
1960, 7210, 5810, 9450, 8910, 3470, 7350, 8410, 7520, 9610,
5150, 2630, 5610, 2750, 7050, 3350, 9450, 7140, 4170, 3090])
salary.hist()
# cut을 사용하기 위해 우선 구간 지정해주기
bins = [0, 2000, 4000, 6000, 8000, 10000]
# cut 함수에 데이터와 구간을 입력하면 데이터를 구간별로 나눠줍니다.
ctg = pd.cut(salary, bins=bins)
# 구간별로 값이 몇개가 속해 있는 지 value_counts()로 확인할 수 있습니다.
ctg.value_counts().sort_index()
# qcut은 구간을 일정하게 나누는 것이 아니라 데이터의 분포를 비슷한 크기의 그룹으로 나눠줍니다.
ctg = pd.qcut(salary, q=5)
💯 본 글에 작성된 전처리 방법을 모든 데이터에 일괄적으로 적용해야하는 것이 아닌 데이터 전처리는 데이터의 특성을 파악해서 해야합니다.