✍🏻 19일 공부 이야기.
문자형 데이터를 숫자형 데이터로 변환시켜주는 도구
import pandas as pd
df = pd.DataFrame({
'A': ['a', 'b', 'c', 'a', 'b'],
'B' : [1,2,3,1,9]
})
df
💻 출력
위와 같은 데이터 프레임이 있다. 'A' 컬럼의 데이터 값들을 숫자형으로 바꾸고 싶을 때 Label Encoder
를 사용하면 된다.
📌 순서는 fit
-> transform
-> 데이터프레임에 적용
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit(df['A'])
fit
시켜주면 le 학습기에 데이터들이 들어가있다.
# 데이터 프레임에 적용
df['le_A'] = le.transform(df['A']) # array([0, 1, 2, 0, 1])
df
이를 데이터프레임에 적용시키면
문자형이 숫자형으로 바뀐 것을 볼 수 있다.
만약 fit
과 transform
을 한 번에 하고 싶다면
le.fit_transform(df['A'])
을 이용하면 된다.
추가적으로
le.transform(['a'])
는 기존의 값이 어떤 값으로 바뀌었는지 살펴볼 수 있다. 이 때 만약 fit되지 않은 값을 넣게 되면(예를 들어 le.transform(['d'])
) 에러가 뜬다.
le.inverse_transform(df['le_A'])
는
transform
된 내용을 다시 원상태로 돌려놓을 수도 있다.
주어진 값을 최소값으로 뺀 후 최대값과 최소값의 차이로 이를 나누어 준다.
=
이 때 는 최소값을 0으로 만드는 역할을 하며
는 크기를 1로 만들어 최대값을 1로 만드는 역할을 한다.
하는 방식은 위와 같다.
변환기 생성 -> fit
-> transform
-> 데이터프레임에 적용
from sklearn.preprocessing import MinMaxScaler
mms = MinMaxScaler()
mms.fit(df)
df_mms = mms.transform(df)
df_mms # min-max scaler가 적용된 데이터
# fit과 transform을 한 번에
#mms.fit_transform(df)
💻 출력
똑같이 inverse_transform
을 통해 역변환할 수 있으며
각 컬럼의 fit
된 값들 또한 확인할 수 있다.
데이터를 표준정규분포 형태로 만들어준다.
=
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(df)
df_ss = ss.transform(df)
df_ss
# fit과 transform을 한 번에
#ss.fit_transform(df)
💻 출력
각 컬럼의 fit된 값 | 역변환 |
---|
형태로 만들어준다.
이 형태의 의미는 아래와 같다.
: 데이터의 중앙값을 0으로 만들고
: 데이터의 25% ~ 75% 까지의 데이터의 길이를 1로 보자.
지금까지 공부한 3가지 스케일러를 비교해보자.
from sklearn.preprocessing import MinMaxScaler, StandardScaler, RobustScaler
mm = MinMaxScaler()
ss = StandardScaler()
rs = RobustScaler()
df_scaler = df.copy()
df_scaler['MinMax'] = mm.fit_transform(df)
df_scaler['Standard'] = ss.fit_transform(df)
df_scaler['Robust'] = rs.fit_transform(df)
df_scaler
💻 출력
각 스케일러를 적용한 데이터프레임이다.
이해를 더 쉽게 하기 위해 boxplot을 그려보았다.
위 시각화된 내용을 분석해보면
원본 데이터에 이상치(5)가 존재한 것으로 보인다.
각 스케일러의 특징을 살펴보자면 아래와 같다.
MinMax : 최소값을 0으로 최대값을 1로 만들어준다. 이런 특성 때문에 이상치의 영향을 많이 받는다.
Standard : 평균을 0으로 만들어준다. 이런 특성 떄문에 이상치의 영향을 많이 받는다.
Robust : 중앙값을 0으로 을 1로 만들어준다. 따라서 위의 두 스케일러보단 이상치의 영향을 덜 받아 데이터의 밀집도가 원 데이터랑 비슷하다는 특징이 있다.
그렇다고 무조건 Robust를 써야한다!! 는 아닐 것이다.
이 또한 데이터의 분포 형태를 보고 데이터 분석가의 몫😊😊
"레드 와인과 화이트 와인을 구분할 수 있을까?"
데이터는 아래 사이트에서 읽어들여왔다.
https://github.com/PinkWink/ML_tutorial/tree/master/dataset
# 데이터 읽기
import pandas as pd
red_url = 'https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/winequality-red.csv'
white_url = 'https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/winequality-white.csv'
red_wine = pd.read_csv(red_url, sep = ';')
white_wine = pd.read_csv(white_url, sep = ';')
두 데이터의 구조가 동일하다. 그러므로 분석을 위해 하나의 데이터프레임으로 만들어주자. 이때, 레드 와인과 화이트 와인을 구분할 수 있는 컬럼을 추가해주어야한다.
# 와인 색상 구분 컬럼
red_wine['color'] = 1
white_wine['color'] = 0
# 두 데이터 합치기
wine = pd.concat([red_wine, white_wine])
wine.info()
레드, 화이트 와인별 등급 분포는 어떻게 되어있을까?
위 차트를 보아, 레드 와인 데이터의 비중이 화이트 와인 데이터의 비중보다 적고
두 와인 모두 5,6등급의 와인 데이터가 많은 것을 알 수 있다.
이제, 레드 와인과 화이트 와인을 분류하는 모델을 만들어보자.
X = wine.drop(['color'], axis=1)
y = wine['color'] # 타겟 데이터
# 학습용 / 테스트용 데이터 분리
from sklearn.model_selection import train_test_split
import numpy as np
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=13)
# 데이터에 무엇이 들어가있고, 각각의 개수가 얼만큼 있는지 확인 가능
np.unique(y_train, return_counts=True)
타켓 데이터인 color
필드를 제외한 데이터는 X에, 타켓 데이터는 y로 두고 학습용 / 테스트용 데이터를 분리시켜주었다.
지금은 사용하지 않았지만
저번 시간에 타켓 데이터의 비율을 학습용/테스트용 데이터에 고르게 하기 위해 배운 stratify
을 사용하는 것이 좋다.
이제 레드 와인과 화이트 와인을 구분하기 위한 분류기를 생성해보자.
# Decision Tree
from sklearn.tree import DecisionTreeClassifier
wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train) # 학습
# Train/Test 데이터에 대한 성능을 평가해보자
from sklearn.metrics import accuracy_score
# 예측
y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)
# 성능
accuracy_score(y_train, y_pred_tr), accuracy_score(y_test, y_pred_test)
별다른 전처리 없이도 95%의 좋은 성능을 가진 분류기가 나왔다.
하지만 이번 시간에 배운 전처리 내용을 더 활용해보자.
먼저 말하자면 DecisionTree 모델은 이번에 배운 스케일러를 한다고 성능이 크게 달라지진 않는다. 그렇지만 한 번 느낌만 살짝 맛보자!!
기존 데이터의 분포는 아래와 같다.
fig = go.Figure()
fig.add_trace(go.Box(y = X['fixed acidity'], name = 'fixed acidity'))
fig.add_trace(go.Box(y = X['chlorides'], name = 'chlorides'))
fig.add_trace(go.Box(y = X['quality'], name = 'quality'))
fig.show()
각 컬럼들의 데이터 분포를 보았는데, 컬럼간 데이터의 분포의 격차가 커보이는 경우 학습이 제대로 안 될 수도 있다.(잘 될수도 있고) 그래서 그것을 막고자 스케일러를 하는 것이다.
from sklearn.preprocessing import MinMaxScaler, StandardScaler
mms = MinMaxScaler()
ss = StandardScaler()
ss.fit(X)
mms.fit(X)
X_ss = ss.transform(X)
X_mms = mms.transform(X)
X_ss_pd = pd.DataFrame(X_ss, columns = X.columns)
X_mms_pd = pd.DataFrame(X_mms, columns = X.columns)
Standard | MinMax |
---|
데이터들이 스케일된 것을 볼 수 있다.
이 데이터로 이제 분류기를 학습해보고 성능을 평가해보자.
Standard | MinMax |
---|
앞서 말한 것처럼 크게 성능이 차이가 나지 않았다.
그래도 다른 데이터에서는 (특히 Cost Function을 최적화할 때) 유효할 수도 있으니 여러 스케일러 중 어떤 것이 효과적인지 살펴볼 필요는 있다.
번외로 모델을 생성할 때 주의해야할 점이 있다.
만약 위 데이터의 quality
컬럼을 이용해 와인을 맛있다
와 맛없다
로 분류할 수 있는 분류기를 만들고자 한다고 해보자.
# quality 컬럼을 이진화
wine['taste'] = [1. if grade > 5 else 0. for grade in wine['quality']]
임의로 quality
가 5 초과인 데이터들에 대해서는 맛있다(1)
를 부여하고 5 이하인 데이터들에 대해서는 맛없다(0)
를 부여했다.
위 데이터 프레임 중 타겟 데이터인 taste
필드를 y에 두고 똑같이 Decision Tree를 돌려보면
100% 의 성능이 나온다...!?!
💡 여기서 주의해야할 점.
원본 데이터를 이용해 다른 필드를 생성할 땐, 이용한 원본 데이터 필드가 데이터 분석에 영향을 주게 되므로 해당 필드를 삭제해주어야한다.
quality
필드까지 삭제한 X 데이터에 대해 분류기를 돌리면 70 % 대의 성능을 가진 분류기가 만들어진 것을 볼 수 있다 :)