데이터 불러오기 및 합치기
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.head()
white_wine.head()

red_wine['color'] = 1
white_wine['color'] = 0
wine = pd.concat([red_wine, white_wine])
wine.info

quality의 속성 및 분포도 확인
wine['quality'].unique()
wine['quality'].value_counts()
import plotly.express as px
fig = px.histogram(wine, x='quality')
fig.show()


레드/화이트 와인별 등급 확인
fig = px.histogram(wine, x='quality', color='color')
fig.show()

레드/화이트 와인 분류기_데이터분리(훈련용, 테스트용) 및 시각화 확인
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)

import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Histogram(x=X_train['quality'], name = 'Train'))
fig.add_trace(go.Histogram(x=X_test['quality'], name = 'Test'))
fig.update_layout(barmode = 'overlay')
fig.update_traces(opacity = 0.75)
fig.show()

결정나무(DecisionTree) 훈련 및 학습 결과
from sklearn.tree import DecisionTreeClassifier
wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train)
from sklearn.metrics import accuracy_score
y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)
print('Train Acc : ', accuracy_score(y_train, y_pred_tr))
print('Test Acc : ', accuracy_score(y_test, y_pred_test))

와인데이터의 특성 확인(boxplot)
X.columns
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()
- 컬럼들의 범위의 격차가 심하면 교육이 제대로 안될 수도 있다
- 최대/최소 범위가 다르고 평균과 분산이 각각 다르다
- 특성의 편향 문제는 최적의 모델을 찾는데 방해가 될 수도 있다

MinMaxScaler와 StandardScaler 적용 미 시각화
from sklearn.preprocessing import MinMaxScaler, StandardScaler
MMS = MinMaxScaler()
SS = StandardScaler()
X_ss = SS.fit_transform(X)
X_mss = MMS.fit_transform(X)
X_ss_pd = pd.DataFrame(X_ss, columns=X.columns)
X_mms_pd = pd.DataFrame(X_mss, columns=X.columns)
- 결정나무에서는 이런 전처리는 의미를 가지진 않는다
- 주로 cost function을 최적화할 때 유효한 경우가 있다
- MinMaxScaler와 StandardScaler 적용해봐야 어느 것이 효율적인지 알 수 있다
MinMaxScaler 시각화
- 최대/최소값을 강제로 1과 0으로 설정하는 것
fig = go.Figure()
fig.add_trace(go.Box(y=X_mms_pd['fixed acidity'], name='fixed acidity'))
fig.add_trace(go.Box(y=X_mms_pd['chlorides'], name='chlorides'))
fig.add_trace(go.Box(y=X_mms_pd['quality'], name='quality'))
fig.show()

StandardScaler 시각화
- 최대/최소값을 강제로 1과 0으로 설정하는 것
def px_box(target_pd):
fig = go.Figure()
fig.add_trace(go.Box(y=target_pd['fixed acidity'], name='fixed acidity'))
fig.add_trace(go.Box(y=target_pd['chlorides'], name='chlorides'))
fig.add_trace(go.Box(y=target_pd['quality'], name='quality'))
fig.show()
px_box(X_ss_pd)

MinMaxScaler와 StandardScaler 각가 결정나무(DecisionTree) 학습 후 acc 확인
X_train, X_test, y_train, y_test = train_test_split(X_mms_pd, y, test_size=0.2, random_state=13)
wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train)
y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)
print('Train ACC : ', accuracy_score(y_train, y_pred_tr))
print('Test ACC : ', accuracy_score(y_test, y_pred_test))
X_train, X_test, y_train, y_test = train_test_split(X_ss_pd, y, test_size=0.2, random_state=13)
wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train)
y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)
print('Train ACC : ', accuracy_score(y_train, y_pred_tr))
print('Test ACC : ', accuracy_score(y_test, y_pred_test))

결정나무(DecisionTree)에 시각화로 화이트와인과 레드와인을 구분하는 특성 확인
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(8, 8))
feature_names_list = X.columns.tolist()
plot_tree(wine_tree, feature_names=feature_names_list)
- total sulfur dioxide 를 첫번째 중요 feature로 residual sugar 를 두번쨰 중요 feature로 설정

feature_importances 이용한 중요 특성 확인
dict(zip(X_train.columns, wine_tree.feature_importances_))

quality를 이진화 하여 taste 컬럼 추가
wine['taste'] = [1.0 if grade > 5 else 0.0 for grade in wine['quality']]
wine.head()

quality 컬럼을 지우지 않을 시 정확도가 왜 100% 인가? 이건 불가능하다
- quality를 feature에서 지우지 않았기 때문에 taste의 기준이 되었던 quality로 학습을 진행하여 정확도가 100%가 나올수 있었다
- 잘못된 feature 설정에 의하여 잘못된 학습을 진행한 경우이다
- 정확도가 1이면 의심해 보아야하는 경우가 있는 것이다

quality 삭제 후 재학습
X = wine.drop(['taste', 'quality'], axis = 1)
y = wine['taste']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=13)
wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train)
y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)
print('Train ACC : ', accuracy_score(y_train, y_pred_tr))
print('Test ACC : ', accuracy_score(y_test, y_pred_test))

어떤 와인이 맛있다고 할 수 있을까?
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(8, 8))
feature_names_list = X.columns.tolist()
plot_tree(wine_tree, feature_names=feature_names_list,
rounded=True, filled=True)
- 결정나무는 와인을 평가할때 alcohol을 가장 첫번째 기준으로 꼽았다
- 결국에 와인의 다양한 속성들이 있지만 알콜의 도수가 맛있는 와인을 평가하는 첫번째 기준이 되었다
