Python을 이용한 Decision Tree (3)

DT Assignment1

주의사항

  • 본인이 구현한 함수임을 증명하기 위해 주석 꼼꼼히 달아주세요.

  • 이 데이터셋 뿐만 아니라 변수의 class가 더 많은 데이터에도 상관없이 적용 가능하도록 함수를 구현해 주세요.

    변수의 class가 3개를 넘는 경우 모든 이진분류 경우의 수를 따져 보아야 합니다.

    Hint) itertools 라이브러리의 combination 함수 & isin 함수 등이 활용될 수 있으며 이 밖에도 본인의 방법대로 마음껏 구현해주세요.

  • 함수에 들어가는 변수나 flow 등은 본인이 변경해도 무관하며 결과만 똑같이 나오면 됩니다.

우수과제 선정이유

코드 한줄한줄 주석을 굉장히 자세하게 정성스레 적어주셔서 다른 분들이 참고하기 좋기 때문에 우수과제로 선정되었습니다.

Data Loading

In [1]:

import pandas as pd 
import numpy as np

In [2]:

pd_data = pd.read_csv('https://raw.githubusercontent.com/AugustLONG/ML01/master/01decisiontree/AllElectronics.csv')
pd_data.drop("RID",axis=1, inplace = True) #RID는 그냥 순서라서 삭제

In [3]:

pd_data

Out[3]:

age

income

student

credit_rating

class_buys_computer

0

youth

high

no

fair

no

1

youth

high

no

excellent

no

2

middle_aged

high

no

fair

yes

3

senior

medium

no

fair

yes

4

senior

low

yes

fair

yes

5

senior

low

yes

excellent

no

6

middle_aged

low

yes

excellent

yes

7

youth

medium

no

fair

no

8

youth

low

yes

fair

yes

9

senior

medium

yes

fair

yes

10

youth

medium

yes

excellent

yes

11

middle_aged

medium

no

excellent

yes

12

middle_aged

high

yes

fair

yes

13

senior

medium

no

excellent

no

1. Gini 계수를 구하는 함수 만들기

  • Input: df(데이터), label(타겟변수명)

  • 해당 결과는 아래와 같이 나와야 합니다.

In [4]:

def get_gini(df, label):
    gini = 1 - (len(df.loc[df[label] == 'yes'])/len(df))**2 - (len(df.loc[df[label] =='no'])/len(df))**2 
    return gini
#타겟변수에 대한 값이 yes일때와 no일대 각각 그 확률값을 제고배서 빼주는 방식이므로
#loc을 통해 그 값만 가지는 row들만 추출해서 그 개수를 새는 식으로 함수를 짰습니다

In [5]:

get_gini(pd_data,'class_buys_computer')

Out[5]:

0.4591836734693877

2. Feature의 Class를 이진 분류로 만들기

ex) {A,B,C} -> ({A}, {B,C}), ({B}, {A,C}), ({C}, {A,B})

  • Input: df(데이터), attribute(Gini index를 구하고자 하는 변수명)

  • 해당 결과는 아래와 같이 나와야 합니다.

In [6]:

import itertools # 변수의 모든 클래시 조합을 얻기 위해 itertools 불러오기
def get_binary_split(df, attribute):
    
    '''
        이 부분을 채워주세요
                           '''
    
    a = list(df[attribute].unique())
    #타겟변수에 대한 값들이 어떤게 있는지 리스트로 만들기 위해 unique를 사용했습니다
    unique =[]
    for i in range(len(a)):
        unique.append([a[i]])
        #각각의 리스트 속 값들을 []또 리스트 안에 넣기 위한 코드입니다

    binary = list(itertools.combinations(a,2))
    #itertools의 combinations을 사용해서 이진분류로 만들었고
        
    for i in range(len(binary)):
        binary[i] = list(map(str,binary[i]))
    #각각의 값들을 또 list로 만들었습니다
    
    result = unique+list(binary)
    #두개를 합쳐서 이진 분류로 완성
    
    return result 

In [7]:

get_binary_split(pd_data, "age")

Out[7]:

[['youth'],
 ['middle_aged'],
 ['senior'],
 ['youth', 'middle_aged'],
 ['youth', 'senior'],
 ['middle_aged', 'senior']]

3. 다음은 모든 이진분류의 경우의 Gini index를 구하는 함수 만들기

  • 위에서 완성한 두 함수를 사용하여 만들어주세요!

  • 해당 결과는 아래와 같이 나와야 합니다.

  • 결과로 나온 Dictionary의 Key 값은 해당 class 들로 이루어진 tuple 형태로 들어가 있습니다.

In [8]:

def get_attribute_gini_index(df, attribute, label):
    
    binary_split = get_binary_split(df,attribute)
    #위에서 구한 함수를 사용해서 split한 변수들을 list로 받습니다
    
    #(멘토링에서 도움 받음.)

    result  = {}
    for i in binary_split:
        unique = list(df[attribute].unique())
        #unique함수를 이용해서 각 타겟변수에 대한 값들을 unique로 받습니다
        for j in range(len(i)):
            if j == 0:
                #binary_split에서 'youth'하나만 고려하는 것처럼 그런 경우를 먼저 
                #그에 해당하는 row들만 뽑아냅니다
                new1 = df.loc[df[attribute] == i[j]]
            else:
                #여러개를 동시에 봐야하는 경우에는 두 경우 모두를 각각 concat으로 더해
                #그 경우의 row들만 뽑아냅니다
                new1 = pd.concat([new1,df.loc[df[attribute]==i[j]]], axis=0)
        
        gini1 = (len(new1[attribute])/len(df[attribute]))* get_gini(new1,label)
        #그래서 앞서 구한 새로운 new data를 통해 gini계수를 구하고 거기에 di/d 확률을 곱해줍니다
        
        # 그다음은 앞서 나눈 것의 나머지 부분을 확인해 gini계수를 구해야하는데
        #위에 방식과 똑같이 진행하면 된다
        #앞서 확인한 값제외한 걸로 확인을 해야하므로 그걸 확인시켜주기 위해 그걸 지우고!
        for k in i:
            unique.remove(k)
        
        for s in range(len(unique)):
            if s == 0:
                new2 = df.loc[df[attribute] == unique[s]]
            else:
                new2 = pd.concat([new2,df.loc[df[attribute]==unique[s]]], axis = 0)
        gini2 = (len(new2[attribute])/len(df[attribute])) *get_gini(new2,label)
        #똑같은 방식으로 두번째 gini계수를 구해준다
        
        #앞서 구한 두 gini계수를 더해주면 된다
        gini = gini1 + gini2
        
        #결과로 나온 dictionary의 key값은 해당 class들로 이루어진 tuple형태로 되어있으므로 tuple()을 취해준다
        result[tuple(i)] = gini
        
    return result                    
                
                

In [9]:

get_attribute_gini_index(pd_data, "age", "class_buys_computer")

Out[9]:

{('youth',): 0.3936507936507937,
 ('middle_aged',): 0.35714285714285715,
 ('senior',): 0.4571428571428572,
 ('youth', 'middle_aged'): 0.4571428571428572,
 ('youth', 'senior'): 0.35714285714285715,
 ('middle_aged', 'senior'): 0.3936507936507937}

여기서 가장 작은 Gini index값을 가지는 class를 기준으로 split해야겠죠?

결과를 확인해보도록 하겠습니다.In [10]:

my_dict = get_attribute_gini_index(pd_data, "age", "class_buys_computer")
key_min = min(my_dict.keys(), key=(lambda k: my_dict[k]))
print('Min -',key_min, ":", my_dict[key_min])
Min - ('middle_aged',) : 0.35714285714285715

다음의 문제를 위에서 작성한 함수를 통해 구한 값으로 보여주세요!

문제1) 변수 ‘income’의 이진분류 결과를 보여주세요.

문제2) 분류를 하는 데 가장 중요한 변수를 선정하고, 해당 변수의 Gini index를 제시해주세요.

문제3) 문제 2에서 제시한 feature로 DataFrame을 split한 후 나눠진 2개의 DataFrame에서 각각 다음으로 중요한 변수를 선정하고 해당 변수의 Gini index를 제시해주세요.

In [11]:

##문제1 답안

get_binary_split(pd_data, "income")

Out[11]:

[['high'],
 ['medium'],
 ['low'],
 ['high', 'medium'],
 ['high', 'low'],
 ['medium', 'low']]

In [12]:

##문제2 답안
#모든 변수들을 모두 gini index를 확인해서 min값을 찾아서 비교했습니다
get_attribute_gini_index(pd_data, "age", "class_buys_computer")

Out[12]:

{('youth',): 0.3936507936507937,
 ('middle_aged',): 0.35714285714285715,
 ('senior',): 0.4571428571428572,
 ('youth', 'middle_aged'): 0.4571428571428572,
 ('youth', 'senior'): 0.35714285714285715,
 ('middle_aged', 'senior'): 0.3936507936507937}

In [13]:

my_dict = get_attribute_gini_index(pd_data, "age", "class_buys_computer")
key_min = min(my_dict.keys(), key=(lambda k: my_dict[k]))
print('Min -',key_min, ":", my_dict[key_min])
#0.3571
Min - ('middle_aged',) : 0.35714285714285715

In [14]:

get_attribute_gini_index(pd_data, "income", "class_buys_computer")

Out[14]:

{('high',): 0.4428571428571429,
 ('medium',): 0.4583333333333333,
 ('low',): 0.45,
 ('high', 'medium'): 0.45,
 ('high', 'low'): 0.4583333333333333,
 ('medium', 'low'): 0.4428571428571429}

In [15]:

my_dict = get_attribute_gini_index(pd_data, "income", "class_buys_computer")
key_min = min(my_dict.keys(), key=(lambda k: my_dict[k]))
print('Min -',key_min, ":", my_dict[key_min])
#0.4428
Min - ('high',) : 0.4428571428571429

In [16]:

get_attribute_gini_index(pd_data, "student", "class_buys_computer")

Out[16]:

{('no',): 0.3673469387755103,
 ('yes',): 0.3673469387755103,
 ('no', 'yes'): 0.7040816326530612}

In [17]:

my_dict = get_attribute_gini_index(pd_data, "student", "class_buys_computer")
key_min = min(my_dict.keys(), key=(lambda k: my_dict[k]))
print('Min -',key_min, ":", my_dict[key_min])
#0.3673
Min - ('no',) : 0.3673469387755103

In [18]:

get_attribute_gini_index(pd_data, "credit_rating", "class_buys_computer")

Out[18]:

{('fair',): 0.42857142857142855,
 ('excellent',): 0.42857142857142855,
 ('fair', 'excellent'): 0.673469387755102}

In [19]:

my_dict = get_attribute_gini_index(pd_data, "credit_rating", "class_buys_computer")
key_min = min(my_dict.keys(), key=(lambda k: my_dict[k]))
print('Min -',key_min, ":", my_dict[key_min])
#0.4285
Min - ('fair',) : 0.42857142857142855

In [ ]:

#확인결과 가장 작은 gini index는
#min -> middle_agged -> 0.3571 이므로
#가장 중요한 변수 : age임을 알수 있습니다

In [20]:

##문제3 답안
#문제3) 문제 2에서 제시한 feature로 DataFrame을 split한 후 나눠진 2개의 
#DataFrame에서 각각 다음으로 중요한 변수를 선정하고 해당 변수의 Gini index를 제시해주세요

#앞서 구한 가장 중요한 변수를 기반으로
#age, middle_aged / youth,senior 이렇게 나누어 새로운 데이터를 만들었습니다

df1 = pd_data.loc[pd_data['age'] == 'middle_aged']
youth = pd_data.loc[pd_data['age'] == 'youth'] 
senior = pd_data.loc[pd_data['age'] == 'senior']
df2 = pd.concat([youth,senior],axis=0)    
#df1은 이미 모든 target 변수 값이 yes인 완벽하게 split된 데이터이므로 확인할 필요가 없고
#df2를 사용해서 다음으로 중요한 변수를 선정했습니다

In [21]:

get_attribute_gini_index(df2, "income", "class_buys_computer")

Out[21]:

{('high',): 0.375,
 ('medium',): 0.48,
 ('low',): 0.47619047619047616,
 ('high', 'medium'): 0.47619047619047616,
 ('high', 'low'): 0.48,
 ('medium', 'low'): 0.375}

In [22]:

my_dict = get_attribute_gini_index(df2, "income", "class_buys_computer")
key_min = min(my_dict.keys(), key=(lambda k: my_dict[k]))
print('Min -',key_min, ":", my_dict[key_min])
#0.375
Min - ('high',) : 0.375

In [23]:

get_attribute_gini_index(df2, "student", "class_buys_computer")

Out[23]:

{('no',): 0.31999999999999984,
 ('yes',): 0.31999999999999984,
 ('no', 'yes'): 0.6599999999999999}

In [24]:

my_dict = get_attribute_gini_index(df2, "student", "class_buys_computer")
key_min = min(my_dict.keys(), key=(lambda k: my_dict[k]))
print('Min -',key_min, ":", my_dict[key_min])
#0.31944444
Min - ('no',) : 0.31999999999999984

In [25]:

get_attribute_gini_index(df2, "credit_rating", "class_buys_computer")

Out[25]:

{('fair',): 0.4166666666666667,
 ('excellent',): 0.4166666666666667,
 ('fair', 'excellent'): 0.7666666666666666}

In [26]:

my_dict = get_attribute_gini_index(df2, "credit_rating", "class_buys_computer")
key_min = min(my_dict.keys(), key=(lambda k: my_dict[k]))
print('Min -',key_min, ":", my_dict[key_min])
#0.416667
Min - ('fair',) : 0.4166666666666667

In [28]:

#확인결과 가장 작은  gini index 갖는 변수 는 student로
#->{('no',): 0.31999999999999984 가장 낮게 나왔습니다

#한번더 gini index를 확인하면
get_attribute_gini_index(df2, "student", "class_buys_computer")

Out[28]:

{('no',): 0.31999999999999984,
 ('yes',): 0.31999999999999984,
 ('no', 'yes'): 0.6599999999999999}

Last updated