이동식 저장소

Data Cleansing 본문

Secondary/Python

Data Cleansing

해스끼 2020. 1. 15. 15:24

오늘은 새로운 걸 배우는 것보다 생각을 하는 시간이 될 겁니다. 마치 이 블로그 주소처럼 말입니다.


데이터 분석의 첫 번째 과정은 데이터를 얻는 것으로부터 시작됩니다. 물론 우리의 예측 불가능한 세상에서 내가 원하는 대로 데이터를 얻을 수 있다는 보장은 없습니다. 예를 들어 내 방의 온도를 10분 간격으로 측정하는데, 데이터가 한두 개 정도 빠질 수도 있고, 이것이 전혀 이상한 일이 아니라는 것이죠. 물론 이러한 경우 말고도 데이터에 관한 다양한 이슈가 있지만, 어쨌든 raw data 그 자체로 데이터를 분석하기란 참 어렵습니다.

 

그래서 우리는 데이터를 가공해야 합니다. 데이터 가공에 대한 몇 가지 이슈를 소개하자면 다음과 같습니다.

- 특정 데이터의 범위가 매우 커서, y값에 미치는 영향이 다른 변수보다 크다면?
- 잘못 기입됐거나 없는 값은 어떻게?
- outlier(다른 값과 극단적으로 떨어진 값)들을 그냥 놔둬도 괜찮을까?

오늘은 일단 간단한 문제 몇 개만 해결해 보도록 하겠습니다.


import numpy as np
import pandas as pd

데이터가 빠진 경우 - 결측치 처리

몇몇 데이터가 빠진 sample(여기서는 row 대신 sample이라는 단어를 사용하겠습니다)을 처리하는 가장 간단한 방법은 drop하는 겁니다. 그런데 100개의 column 중 하나만 빠졌다고 해서 drop하면, 살아남은 데이터가 몇 안 될지도 모릅니다. 그래서 사람들은 조금 더 온건한 방법을 생각했습니다.

- "몇 개는 봐준다": n개 이상의 column이 누락된 sample만 drop
- 데이터가 거의 없는 column은 column 자체를 drop
- 빈 데이터를 평균값, 중앙값 등으로 채우기

 

다음의 데이터를 가정합시다.

# Example from - https://chrisalbon.com/python/pandas_missing_data.html
raw_data = {'first_name': ['Jason', np.nan, 'Tina', 'Jake', 'Amy'],
            'last_name': ['Miller', np.nan, 'Ali', 'Milner', 'Cooze'],
            'age': [42, np.nan, 36, 24, 73],
            'sex': ['m', np.nan, 'f', 'm', 'f'],
            'preTestScore': [4, np.nan, np.nan, 2, 3],
            'postTestScore': [25, np.nan, np.nan, 62, 70]}
df = pd.DataFrame(raw_data, columns=[
                  'first_name', 'last_name', 'age', 'sex', 'preTestScore', 'postTestScore'])
print(df)

스파이가 있다

중간중간에 null이 보입니다. 심지어 1번 데이터는 몽땅 null이네요. 데이터에서 null을 몽땅 거르려면, 즉 하나라도 null이 있는 row를 전부 drop하려면 이 코드를 쓰면 됩니다.

# has at least one null...
df_no_missing = df.dropna()
print(df_no_missing)

원본은 바뀌지 않는다.

null 값이 있었던 1, 2번 데이터가 없어졌습니다. 물론 원본 데이터(df)에는 그대로 남아있습니다.

 

그런데 위에서도 말했듯이 한 두개의 null을 트집잡아서 drop하는 건 조금 가혹한 것 같습니다. 인덱스 1번처럼 전부 null인 데이터만 drop하고 싶다면 how 매개변수를 지정하면 됩니다.

# only if all data is null
df_cleaned = df.dropna(how='all')
print(df_cleaned)

이것보다는 조금 더 걸러내는 방법으로, null의 개수를 제한하는 방법이 있습니다. 즉 null이 아닌 column이 최소 n개 이상일 때에만 살려줄 수 있습니다.

print(df.dropna(thresh=n)) # (not null) >= n

 

위에서 남은 null을 어떻게든 활용하기 위해서는 어떤 값이든 채워 넣어야 합니다. 물론 문자 그대로 랜덤값을 넣는 것은 아무 의미가 없고, column의 대표값을 넣는 것이 그나마 가장 정확한 예측이 되겠죠.

 

이때 활용할 수 있는 대표값으로는 평균, 중간값, 최빈값이 있습니다. pandas에서는 이러한 값들을 쉽게 얻을 수 있습니다.

df['preTestScore'].mean()    # 평균
df['preTestScore'].median()  # 중간값
df['preTestScore'].mode()    # 최빈값

이 값들을 이용하여 null을 채우면 됩니다. 위의 데이터에서 preTestScore의 null에 평균값을 채워넣어 보겠습니다.

df['preTestScore'].fillna(df['preTestScore'].mean(), inplace=True)
print(df)

fillna() 함수는 null을 매개변수의 값으로 채워줍니다. 데이터와 상황에 맞게 적절한 값을 선택하여 채워 넣도록 합시다.

 

 

데이터의 구간을 나누자 - Data Binning

위에서 본 것보다 더 많은 데이터를 준비했습니다.

# Example from - https://chrisalbon.com/python/pandas_binning_data.html
raw_data = {'regiment': ['Nighthawks', 'Nighthawks', 'Nighthawks', 'Nighthawks', 'Dragoons', 'Dragoons', 'Dragoons', 'Dragoons', 'Scouts', 'Scouts', 'Scouts', 'Scouts'],
            'company': ['1st', '1st', '2nd', '2nd', '1st', '1st', '2nd', '2nd', '1st', '1st', '2nd', '2nd'],
            'name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze', 'Jacon', 'Ryaner', 'Sone', 'Sloan', 'Piger', 'Riani', 'Ali'],
            'preTestScore': [4, 24, 31, 2, 3, 4, 24, 31, 2, 3, 2, 3],
            'postTestScore': [25, 94, 57, 62, 70, 25, 94, 57, 62, 70, 62, 70]}
df = pd.DataFrame(raw_data, columns=[
                  'regiment', 'company', 'name', 'preTestScore', 'postTestScore'])
print(df)

우리는 데이터를 특정 구간으로 나누고, 각 구간마다 함수를 적용할 수 있습니다. 예를 들어 우리는 postTestScore를 25점 간격으로 나누어서 등급(문자열)을 부여할 수 있습니다.

[0, 25] → Bad
(25, 50] → Okay
(50, 75] → Good
(75, 100] → Great
bins = [0, 25, 50, 75, 100]                     # 구간
group_names = ['Bad', 'Okay', 'Good', 'Great']  # 구간 이름

df['categories'] = pd.cut(df['postTestScore'], bins, labels=group_names)
print(df)

pd.cut() 함수를 이용하여 구간마다 label을 적용할 수 있습니다. 점수 구간별로 학점을 부여하는 등의 응용도 가능하겠죠?

 

 

Feature scaling

키와 몸무게를 생각해 봅시다. 일반적으로 몸무게(kg)보다 키(cm)의 값이 더 큽니다. 두 변수의 가중치가 같을 경우, 절댓값이 큰 키가 전체 결과에 더 많은 영향을 미칠 수 있습니다. 물론 가중치를 다르게 하면 되지만, 현실적으로 가중치를 몇으로 정할지는 매우 복잡한 문제이기 때문에 값 자체를 건드리기도 합니다.

 

우리의 목적은 'column간의 최대-최소 차이를 같게 하는 것'입니다. 동시에 데이터의 분포를 바꾸면 안 되겠죠. 이 조건을 만족시키면서 가장 많이 쓰이는 방법은 다음과 같습니다.

 

1) min-max normalization

각기 다른 최솟값-최댓값을 모두 하나의 값으로 통일시키는 방법입니다. 

보통 new min=0, new max=1로 두고 계산합니다.

 

2) Z-score normalization

데이터를 표준정규분포의 Z값으로 바꾸는 방법입니다. new max,min을 몇으로 해야 할지 모르는 경우에 적용하면 좋습니다.


오늘은 뭔가 알듯말듯한 내용을 살펴봤습니다. 저도 방금 공부하고 왔는데 음.. 위의 내용들은 "이런 문제가 있다" 정도로 기억하시면 될 듯 합니다.

 

다음 글에서는 kaggle에 대해 알아보겠습니다.

'Secondary > Python' 카테고리의 다른 글

static method, class method, abstract method  (0) 2023.03.01
matplotlib  (0) 2020.01.12
Pandas (2)  (0) 2020.01.10
Pandas (1)  (0) 2020.01.09
Numpy - Numerical Python  (0) 2020.01.05
Comments