본문 바로가기
Study/혼자 공부하는 판다스

혼자 공부하는 판다스 - 의사결정 나무

by Wanooky 2022. 4. 30.

컴퓨터 알고리즘에서 즐겨 사용하는 트리 구조를 사용하고, 각 노드에는 분석 대상의 속성들이 위치한다.

 

각 분기점마다 목표 값을 가장 잘 분류할 수 있는 속성을 찾아서 배치하고, 해당 속성이 갖는 값을 이용하여 새로운

 

가지를 만든다.

 

각 분기점에서 최적의 속성을 선택할 때는 해당 속성을 기준으로 분류한 값들이 구분되는 정도를 측정한다.

 

주로 엔트로피를 이용하는데, 엔트로피가 낮을수록 분류갸 잘 된 것이다. 

 


데이터 준비

 

암세포 진단 데이터셋을 사용하자. URL을 입력하면 데이터를 다운로드 받을 수 있다.

 

import pandas as pd
import numpy as np

uci_path = 'https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/breast-cancer-wisconsin.data'
df = pd.read_csv(uci_path, header=None)

df.columns = ['id', 'clump', 'cell_size','cell_shape','adhesion','epithlial','bare_nuclei','chromatin','normal_nucleoli','mitoses','class']

pd.set_option('display.max_columns', 15)

print(df.head())
print('\n')

print(df.info())
print('\n')

print(df.describe())

        id  clump  cell_size  cell_shape  adhesion  epithlial bare_nuclei  \
0  1000025      5          1           1         1          2           1   
1  1002945      5          4           4         5          7          10   
2  1015425      3          1           1         1          2           2   
3  1016277      6          8           8         1          3           4   
4  1017023      4          1           1         3          2           1   

   chromatin  normal_nucleoli  mitoses  class  
0          3                1        1      2  
1          3                2        1      2  
2          3                1        1      2  
3          3                7        1      2  
4          3                1        1      2  


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 699 entries, 0 to 698
Data columns (total 11 columns):
 #   Column           Non-Null Count  Dtype 
---  ------           --------------  ----- 
 0   id               699 non-null    int64 
 1   clump            699 non-null    int64 
 2   cell_size        699 non-null    int64 
 3   cell_shape       699 non-null    int64 
 4   adhesion         699 non-null    int64 
 5   epithlial        699 non-null    int64 
 6   bare_nuclei      699 non-null    object
 7   chromatin        699 non-null    int64 
 8   normal_nucleoli  699 non-null    int64 
 9   mitoses          699 non-null    int64 
 10  class            699 non-null    int64 
dtypes: int64(10), object(1)
memory usage: 60.2+ KB
None


                 id       clump   cell_size  cell_shape    adhesion  \
count  6.990000e+02  699.000000  699.000000  699.000000  699.000000   
mean   1.071704e+06    4.417740    3.134478    3.207439    2.806867   
std    6.170957e+05    2.815741    3.051459    2.971913    2.855379   
min    6.163400e+04    1.000000    1.000000    1.000000    1.000000   
25%    8.706885e+05    2.000000    1.000000    1.000000    1.000000   
50%    1.171710e+06    4.000000    1.000000    1.000000    1.000000   
75%    1.238298e+06    6.000000    5.000000    5.000000    4.000000   
max    1.345435e+07   10.000000   10.000000   10.000000   10.000000   

        epithlial   chromatin  normal_nucleoli     mitoses       class  
count  699.000000  699.000000       699.000000  699.000000  699.000000  
mean     3.216023    3.437768         2.866953    1.589413    2.689557  
std      2.214300    2.438364         3.053634    1.715078    0.951273  
min      1.000000    1.000000         1.000000    1.000000    2.000000  
25%      2.000000    2.000000         1.000000    1.000000    2.000000  
50%      2.000000    3.000000         1.000000    1.000000    2.000000  
75%      4.000000    5.000000         4.000000    1.000000    4.000000  
max     10.000000   10.000000        10.000000   10.000000    4.000000

결측값을 처리하고, 결측값이 들어있는 행을 삭제한다. 그리고 bare_nuclei 열을 정수형으로 변환한다.

 

print(df['bare_nuclei'].unique())
print('\n')

df['bare_nuclei'].replace('?', np.nan, inplace=True)
df.dropna(subset=['bare_nuclei'], axis=0, inplace=True)
df['bare_nuclei'] = df['bare_nuclei'].astype('int')

print(df.describe())


['1' '10' '2' '4' '3' '9' '7' '?' '5' '8' '6']


                 id       clump   cell_size  cell_shape    adhesion  \
count  6.830000e+02  683.000000  683.000000  683.000000  683.000000   
mean   1.076720e+06    4.442167    3.150805    3.215227    2.830161   
std    6.206440e+05    2.820761    3.065145    2.988581    2.864562   
min    6.337500e+04    1.000000    1.000000    1.000000    1.000000   
25%    8.776170e+05    2.000000    1.000000    1.000000    1.000000   
50%    1.171795e+06    4.000000    1.000000    1.000000    1.000000   
75%    1.238705e+06    6.000000    5.000000    5.000000    4.000000   
max    1.345435e+07   10.000000   10.000000   10.000000   10.000000   

        epithlial  bare_nuclei   chromatin  normal_nucleoli     mitoses  \
count  683.000000   683.000000  683.000000       683.000000  683.000000   
mean     3.234261     3.544656    3.445095         2.869693    1.603221   
std      2.223085     3.643857    2.449697         3.052666    1.732674   
min      1.000000     1.000000    1.000000         1.000000    1.000000   
25%      2.000000     1.000000    2.000000         1.000000    1.000000   
50%      2.000000     1.000000    3.000000         1.000000    1.000000   
75%      4.000000     6.000000    5.000000         4.000000    1.000000   
max     10.000000    10.000000   10.000000        10.000000   10.000000   

            class  
count  683.000000  
mean     2.699854  
std      0.954592  
min      2.000000  
25%      2.000000  
50%      2.000000  
75%      4.000000  
max      4.000000

그리고 설명 변수 x로 사용할 열들을 선택하고, 예측 변수 y로 사용할 열을 선택한다.

 

그리고, 정규화 실행 후에 훈련 데이터와 검증 데이터를 나눈다.

 

x = df[['clump', 'cell_size', 'cell_shape', 'adhesion', 'epithlial', 'bare_nuclei', chromatin',
        'normal_nucleoli', 'mitoses']]
y = df['class']

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(x)
x = ss.transform(x)

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=10)

print(x_train.shape)
print(x_test.shape)


(478, 9)
(205, 9)

모형 학습 및 검증

 

sklearn 라이브러리의 tree 모듈을 임포트한다. DecisionTreeClassifier() 함수를 사용하여 모형 객체를 생성하고

 

각 분기점에서 최적의 속성을 찾기 위해 분류 정도를 평가하는 기준으로 'entropy' 값을 사용한다.

 

트리 레벨을 5로 지정하는데, 5단계까지 가지를 확장할 수 있음을 의미한다.

 

from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(criterion='entropy', max_depth=5)

dt.fit(x_train, y_train)

y_hat = dt.predict(x_test)

print(y_hat[0:10])
print(y_test.values[0:10])

[4 4 4 4 4 4 2 2 4 4]
[4 4 4 4 4 4 2 2 4 4]

모형을 통해 예측한 값 y_hat와 실제 값 y_test를 비교하면 다음과 같다.

 

이 의사결정 나무의 모형 평가 지표를 출력해보자.

 

from sklearn import metrics
tree_matrix = metrics.confusion_matrix(y_test, y_hat)
print(tree_matrix)
print('\n')

tree_report = metrics.classification_report(y_test, y_hat)
print(tree_report)

[[127   4]
 [  2  72]]


              precision    recall  f1-score   support

           2       0.98      0.97      0.98       131
           4       0.95      0.97      0.96        74

    accuracy                           0.97       205
   macro avg       0.97      0.97      0.97       205
weighted avg       0.97      0.97      0.97       205

양성을 정확히 예측한 TP는 127개, 양성을 악성으로 잘못 분류한 FP는 4개

 

약성을 양성으로 잘못 분류한 FN은 2개, 악성을 정확히 예측한 TN은 72개이다.

 

이를 그림으로 나타내는 매서드는 plot_tree 이다

 

import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10, 7))
plot_tree(dt)
plt.show()