1) Data quality problems
- 데이터의 최대/최소가 다름 -> Scale에 따른 y값에 영향
- Ordinary 또는 Nominal한 값들(범주형 데이터)의 표현은 어떻게?
- 잘못 기입된 값들에 대한 처리
- 값이 없을 경우는 어떻게?
- 극단적으로 큰 값 또는 작은 값들은 그대로 놔둬야 하는가?
2) Data preprocessing issues
기본적으로 아래의 세 가지에 대해 전처리를 한다.
- 데이터가 빠진 경우 (결측치의 처리)
- 라벨링된 데이터(category) 데이터의 처리
- 데이터의 scale의 차이가 매우 크게 날 경우
3) Missing Values
데이터가 없을 때 할 수 있는 전략은 아래와 같다.
◆ 데이터가 없으면 sample을 drop
◆ 데이터가 없는 최소 개수를 정해서 sample을 drop
◆ 데이터가 거의 없는 feature는 feature 자체를 drop
◆ 최빈값, 평균값으로 비어있는 데이터를 채우기
여기서 사용할 데이터는 아래와 같다.
import pandas as pd
import numpy as np
# Eaxmple 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)
>> first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
1 NaN NaN NaN NaN NaN NaN
2 Tina Ali 36.0 f NaN NaN
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0
(1) Data drop
# column별로 NaN이 데이터의 수 합계를 표시
df.isnull().sum
>>
first_name 1
last_name 1
age 1
sex 1
preTestScore 2
postTestScore 2
dtype: int64
# 사실상 개수보다는 전체 데이터의 수에 대한 NaN값의 비율이 더 중요하며 df.isnull().sum / len(df) 코드로 확인가능하다.
# dropna() -> NaN 포함된 데이터들이 사라짐
df_no_missing = df.dropna()
print(df_no_missing)
>>
first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0
# 모든 데이터가 비어 있는 행 drop
df_cleaned = df.dropna(how='all')
print(df_cleaned)
>>
first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
2 Tina Ali 36.0 f NaN NaN
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0
# 모든 데이터가 NaN인 column 생성
df['location'] = np.nan
print(df)
>>
first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
1 NaN NaN NaN NaN NaN NaN NaN
2 Tina Ali 36.0 f NaN NaN NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN
# 축(여기서는 column)을 기준으로 데이터가 최소 4개 이상 없는 column을 drop, axis를 지정하지 않으면 행 기준으로 drop
result = df.dropna(axis=1, thresh=3)
print(result)
>>
first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
1 NaN NaN NaN NaN NaN NaN
2 Tina Ali 36.0 f NaN NaN
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0
# column 기준으로 삭제
result = df.dropna(axis=1, how='all')
print(result)
>>
first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
1 NaN NaN NaN NaN NaN NaN
2 Tina Ali 36.0 f NaN NaN
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0
(2) Data Fill
평균값, 중위값, 최빈값을 활용
◆ 평균값 : 해당 column의 값의 평균을 내서 채우기
df["preTestScore"].mean()
◆ 중위값 : 값을 일렬로 나열 했을 때 중간에 위치한 값
df["preTestScore"].median()
◆ 최빈값 : 가장 많이 나오는 값
df["preTestScore"].mode()
# 데이터가 없는 곳에 0 대입
df.fillna(0)
>> first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 0.0
1 0 0 0.0 0 0.0 0.0 0.0
2 Tina Ali 36.0 f 0.0 0.0 0.0
3 Jake Milner 24.0 m 2.0 62.0 0.0
4 Amy Cooze 73.0 f 3.0 70.0 0.0
# 데이터가 없는 곳에 preTestScore의 평균값 삽입
df["preTestScore"].fillna(df["preTestScore"].mean(), inplace=True)
print(df)
>> first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
1 NaN NaN NaN NaN 3.0 NaN NaN
2 Tina Ali 36.0 f 3.0 NaN NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN
# 성별로 나눠서 평균 값 삽입
df["preTestScore"].fillna(df.groupby("sex")["preTestScore"].transform("mean"), inplace=True)
print(df)
>> first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
1 NaN NaN NaN NaN NaN NaN NaN
2 Tina Ali 36.0 f 3.0 NaN NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN
# Age와 sex가 모두 notnull인 경우에만 표시
print(df[df['age'].notnull() & df['sex'].notnull()])
>> first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
2 Tina Ali 36.0 f NaN NaN NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN
4) Category data
- 이산형 데이터(범주형 데이터)를 어떻게 처리할까? => One-Hot Encoding
{green, blue, yellow} | 데이터 집합 |
인덱스 부여 {Green} -> [ 1, 0, 0 ] {Green} -> [ 1, 0, 0 ] {blue} -> [0, 1, 0] |
실제 데이터 set의 크기만큼 Binary Feature를 생성 |
(1) Data type
import pandas as pd
import numpy as np
edges = pd.DataFrame({'source': [0, 1, 2],
'target': [2, 2, 3],
'weight': [3, 4, 5],
'color': ['red', 'blue', 'blue']})
print(edges)
>> source target weight color
0 0 2 3 red
1 1 2 4 blue
2 2 3 5 blue
# source의 Data의 type = int64
print(["source"])
>> 0 0
1 1
2 2
Name: source, dtype: int64
# color의 Data의 type = obejct
print(edges["color"])
(2) One Hot Encoding
# pandas의 get_dummies()메소드는 obejct를 one hot ecoding시키는 메소드이다.
# get_dummies()메소드가 알아서 object인 color를 기준으로 one hot ecoding시킨 결과이다.
result = pd.get_dummies(edges)
print(result)
>> source target weight color_blue color_red
0 0 2 3 0 1
1 1 2 4 1 0
2 2 3 5 1 0
# edges에서 color만 끊어서도 one hot encoding할 수 있다.
# 데이터가 너무 많은 경우 끊어서 볼 때 유용하게 사용할 수 있다.
result = pd.get_dummies(edges["color"])
print(result)
>> blue red
0 0 1
1 1 0
2 1 0
result = pd.get_dummies(edges[["color"]])
print(result)
>> color_blue color_red
0 0 1
1 1 0
2 1 0
# Ordinary data -> One Hot Encoding
# 구분을 위해 임의로 사이즈 별로 3, 4, 5라는 데이터를 넣었지만
# 컴퓨터가 그 자체를 숫자 값으로 인식하면 안 되기 때문에 category데이터로 변경해줘야한다.
# 그러기 위해서는 dict타입으로 만들어주고, map을 시켜주면 값에 맞게 M, L, XL 컬럼이 추가 된다.
weight_dict = {3:"M", 4:"L", 5:"XL"}
edges["weight_sign"] = edges["weight"].map(weight_dict)
print(edges)
>> source target weight color weight_sign
0 0 2 3 red M
1 1 2 4 blue L
2 2 3 5 blue XL
# 위에서 수정한 데이터를 다시 .get_dummies()를 이용하여 One Hot Encoding시키면 feature을 뽑을 수 있다.
# 그러나 One Hot Encoding은 굉장히 많은 0 데이터를 만들기 때문에 차원의 저주에 걸릴 수 있으므로 best 방식은 아니다.
# 그러므로 One Hot Encoding후 여러 조치를 취하지만 여기서는 다루지 않도록 하겠다.
# 참고로 강의 자료에 있는 edges.as_matrix()는 더 이상 사용불가
edges = pd.get_dummies(edges)
result = edges.to_numpy() # 혹은 pd.get_dummies(edges).values
print(result)
>> [[0 2 3 0 1 0 1 0]
[1 2 4 1 0 1 0 0]
[2 3 5 1 0 0 0 1]]
as_matrix() 메소드의 목적은 frame을 Numpy-array로 표현하는 것이지만 as_matrix() 메소드는 버전 0.23.0이후로 사라졌으니,
0.23.0이후의 버전은 DataFrame.values() 를 대신 사용하도록 권장되고 있다.
즉, frame을 Numpy-array로 표현하기 위한 두 가지 방법이 있다.
(1) .values() : numpy.ndarray를 반환한다.
(2) .to_numpy() : numpy.ndarray를 반환한다.
(3) Data Binning
그래프 상에 퍼져있는 데이터에 대해 구간을 나눠 줄 필요가 있다. 사용할 데이터는 아래와 같다.
import pandas as pd
import numpy as np
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)
>> regiment company name preTestScore postTestScore
0 Nighthawks 1st Miller 4 25
1 Nighthawks 1st Jacobson 24 94
2 Nighthawks 2nd Ali 31 57
3 Nighthawks 2nd Milner 2 62
4 Dragoons 1st Cooze 3 70
5 Dragoons 1st Jacon 4 25
6 Dragoons 2nd Ryaner 24 94
7 Dragoons 2nd Sone 31 57
8 Scouts 1st Sloan 2 62
9 Scouts 1st Piger 3 70
10 Scouts 2nd Riani 2 62
11 Scouts 2nd Ali
# Define bins as 0 to 25, 25 to 50, 50 to 75, 75 to 100 ( 구간에 해당하는 list 생성 )
bins = [0, 25, 50, 75, 100]
group_names = ['Low', 'Okay', 'Good', 'Great'] # 각 구간에 대한 이름을 명명한다.
# Cut후 categories에 할당
categories = pd.cut(df['postTestScore'], bins, labels=group_names)
print(categories)
>>
0 Low
1 Great
2 Good
3 Good
4 Good
5 Low
6 Great
7 Good
8 Good
9 Good
10 Good
11 Good
Name: postTestScore, dtype: category
Categories (4, object): [Low < Okay < Good < Great]
# 기존 dataframe에 할당
df['categories'] = pd.cut(df['postTestScore'], bins, labels=group_names)
result = pd.value_counts(df['categories'])
print(result)
>>
Good 8
Great 2
Low 2
Okay 0
Name: categories, dtype: int64
print(df)
>> regiment company name preTestScore postTestScore categories
0 Nighthawks 1st Miller 4 25 Low
1 Nighthawks 1st Jacobson 24 94 Great
2 Nighthawks 2nd Ali 31 57 Good
3 Nighthawks 2nd Milner 2 62 Good
4 Dragoons 1st Cooze 3 70 Good
5 Dragoons 1st Jacon 4 25 Low
6 Dragoons 2nd Ryaner 24 94 Great
7 Dragoons 2nd Sone 31 57 Good
8 Scouts 1st Sloan 2 62 Good
9 Scouts 1st Piger 3 70 Good
10 Scouts 2nd Riani 2 62 Good
11 Scouts 2nd Ali 3 70 Good
(4) Label encoding by slearn
- 위에서 배운 pandas의 getdummies()메소드 이 외에도 Sckit-learn의 preprocessing 패키지도 label, one-hot지원
raw_example = df.to_numpy()
print(raw_example[:3])
>> [['Nighthawks' '1st' 'Miller' 4 25 'Low']
['Nighthawks' '1st' 'Jacobson' 24 94 'Great']
['Nighthawks' '2nd' 'Ali' 31 57 'Good']]
import pandas as pd
import numpy as np
from sklearn import preprocessing
(상위 코드 생략)
le = preprocessing.LabelEncoder() # Encoder 생성
le.fit(raw_example[:,0]) # Data에 맞게 encoding fitting
result = le.transform(raw_example[:,0]) # 실제 데이터 -> labelling data
print(result)
>> [1 1 1 1 0 0 0 0 2 2 2 2]
- Label encoder의 fit과 transform의 과정이 나눠진 이유는 새로운 데이터 입력시, 기존 labelling규칙을 그대로 적용할 필요가 있기 때문이다.
- Fit 은 규칙을 생성하는 과정
- Transform은 규칙을 적용하는 과정
- Fit을 통해 규칙이 생성된 labelencoder는 따로 저장하여 새로운 데이터를 입력할 경우 사용할 수 있음
- Encoder들을 실제 시스템에 사용할 경우 pickle화 필요
label_column = [0, 1, 2, 5]
label_encoder_list = []
for column_idex in label_column:
le = preprocessing.LabelEncoder()
le.fit(raw_example[:, column_idex])
data[:, column_idex] = le.transform(raw_example[:,column_idex])
label_encoder_list.append(le) # 기존 label encoder를 따로 저장
del le
print(data[:3])
>> [[1 0 4 4 25 2]
[1 0 2 24 94 1]
[1 1 0 31 57 0]]
print(label_encoder_list[0].transform(raw_example[:10,0])) # 저장된 le로 새로운 데이터에 적용
>> [1 1 1 1 0 0 0 0 2 2]
(5) One-hot encoding by sklearn
- Numerice labelling 이 완료된 데이터에 one-hot적용
- 데이터는 1-dim으로 변환하여 넣어 줄 것은 권장
one_hot_enc = preprocessing.OneHotEncoder()
one_hot_enc.fit(data[:,0].reshape(-1,1)) # 1-dim 변환하여 fit
onhotlables = one_hot_enc.transform(data[:,0].reshape(-1,1)).toarray() # 1-dim 변환후 transform -> ndarray
print(onhotlables)
>> [[0. 1. 0.]
[0. 1. 0.]
[0. 1. 0.]
[0. 1. 0.]
[1. 0. 0.]
[1. 0. 0.]
[1. 0. 0.]
[1. 0. 0.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]]
5) Feature scaling
- Feature간의 최대 - 최소값의 차이를 맞춘다.
- 실제 사용할 때는 반드시 정규화 Parameter(최대/최소, 평균/표준편차) 등을 기억하여 새로운 값에 적용해야함
(1) Min - Max Normalization
- 기존 변수에 범위를 새로운 최대-최소로 변경, 일반적으로 0과 1 사이 값으로 변경함
(df["A"] - df["A"].min()) / (df["A"].max()-df["A"].min() * (5-1)+1)
(2) Z-Score Normalization (Standardization)
- 기존 변수에 범위를 정규 분포로 변환, 실제 Min-Max의 값을 모를 때 활용 가능
df["B"] = (df["B"] - df["B"].mean()) \ (df["B"].std())
(3) Feature Scaling Function
def feture_scaling(df, scaling_strategy="min-max", column=None):
if column == None:
column = [column_name for column_name in df.columns]
for column_name in column:
if scaling_strategy == "min-max":
df[column_name] = ( df[column_name] - df[column_name].min() ) /\
(df[column_name].max() - df[column_name].min())
elif scaling_strategy == "z-score":
df[column_name] = ( df[column_name] - \
df[column_name].mean() ) /\
(df[column_name].std() )
return df
(4) Feature scaling with sklearn
- Label encoder와 마찬가지로, sklearn도 feature scale 지원
- MinMaxScaler와 StandarScaler 사용
from sklearn import preprocessing
std_scaler = preprocessing.StandardScaler().fit(df[['Alcohol', 'Malic acid']])
df_std = std_scaler.transform(df[['Alcohol', 'Malic acid']])
df_std[:5]
- Preprocessing은 모두 fit -> transform의 과정을 거침 (이유는 label encoder)와 동일
- 단, scaler는 한번에 여러 column을 처리 가능
minmax_scaler = preprocessing.MinMaxScaler().fit(df[['Alcohol', 'Malic acid']])
minmax_scaler.transform(df[['Alcohol', 'Malic acid']])
df_minmax[:3]
참고자료
https://www.edwith.org/aipython/lecture/24076/
'IT > 언어' 카테고리의 다른 글
[python] python의 sort함수에 사용되는 lambda에 대한 이해 (0) | 2020.08.17 |
---|---|
[python/numpy] Numpy의 데이터형 dtype과 astype를 사용한 변경(캐스트) (0) | 2020.06.05 |
[python] 머신러닝을 위한 Python(7) ; Data Handling - Pandas # 2 (0) | 2020.06.05 |
[python] 머신러닝을 위한 Python(6); Data Handling - Pandas # 1 (0) | 2020.06.04 |
[python] 머신러닝을 위한 Python(5) ; Data handling - Numerical Python (0) | 2020.06.04 |