Python的3id算法 python id3_信息增益

 

Python的3id算法 python id3_信息增益_02

 

ID3算法原理

ID3算法的基本策略如下:

(1)树以代表训练样本的单个节点开始;

(2)如果样本都在同一个类中,则这个节点成为树叶结点并标记为该类别;

(3)否则算法使用信息熵(称为信息增益)作为启发知识来帮助选择合适的将样本分类的属性,以便将样本集划分为若干子集,

(4)对测试属性的每个已知的离散值创建一个分支,并据此划分样本;

(5)算法使用类似的方法,递归地形成每个划分上的样本决策树:

(6)整个递归过程在下列条件之一成立时停止。

ID3算法的核心是在决策树各级结点上选择属性时,用信息增益作为属性的选择标准,以使得在每一个非结点进行测试时,能获得关于被测试记录最大的类别信息

 

python3

from math import log


def createDataSet():
    labels = ['Outlook','Temperature','Humidity','Windy','PlayGolf']

    dataset = [['2', '2', '1', '0', 'no'],         
               ['2', '2', '1', '1', 'no'],
               ['1', '2', '1', '0', 'yes'],
               ['0', '1', '1', '0', 'yes'],
               ['0', '0', '0', '0', 'yes'],
               ['0', '0', '0', '1', 'no'],
               ['1', '0', '0', '1', 'yes'],
               ['2', '1', '1', '0', 'no'],
               ['2', '0', '0', '0', 'yes'],
               ['0', '1', '0', '0', 'yes'],
               ['2', '1', '0', '1', 'yes'],
               ['1', '1', '1', '1', 'yes'],
               ['1', '2', '0', '0', 'yes'],]
    
    return dataset,labels

def entropy(S): #计算熵
    C={}    #初始类别字典,key为类别,value为出现次数
    e=0 #初始化熵

    #为C赋值
    for x in S:
        if x[-1] not in C:  #i为一条数据,最后一项即类别
            C[x[-1]]=1
        else:
            C[x[-1]]+=1

    #计算熵
    for x in C:
        Pi=C[x]/len(S)
        e+=Pi*log(Pi,2)
    
    return -e

def gain(A,S):  #计算信息增益,A为属性下标
    Si={}   #初始化根据属性A划分的子集字典,key为属性A,value为属性A为key的数据集合
    e=0 #初始化子集的熵
    g=0 #初始化信息增益

    #为Si赋值
    for x in S:
        if x[A] not in Si:
            Si[x[A]]=[x]
        else:
            Si[x[A]].append(x)

    #计算子集的熵
    for x in Si:
        e+=len(Si[x])/len(S)*entropy(Si[x])

    g=entropy(S)-e

    return g

def split(S, index, value):    #根据属性划分集合,index为属性下标,value为属性值
    split_dataSet = []  #初始化被划分的子集

    for x in S:
        if x[index] == value:
            #去掉下标为index的属性
            temp=x[:index]
            temp.extend(x[index+1:])
            split_dataSet.append(temp)
     
    return split_dataSet

def choose(S):  #选择最优属性,返回属性下标
    index=0 #初始最优属性下标
    for i in range(len(S[0])-1):
        if gain(i,S)>gain(index,S):
            index=i
    
    return index

def primary_class(S):   #从一个属性为空的样本集中返回出现次数最多的类别
    C={}    #初始类别字典,key为类别,value为出现次数
    
    for x in S:
        for c in x:
            if c not in C:
                C[c]=1
            else:
                C[c]+=1

    primary_class=x[0]
    
    for x in C:
        if C[x]>C[primary_class]:
            primary_class=x

    return primary_class
    

def create_tree(dataset,labels):    #创建决策树,dataset为样本集,label为标签
    C=set()    #初始化类别集合
    for i in dataset:
        C.add(i[-1])

    if len(C)==1:   #如果都属于同一类则返回该类别
        return C.pop()
    
    if len(dataset[0])==1:  #如果属性为空则返回出现次数最多的类别
        return primary_class(dataset)

    best_attribute=choose(dataset)  #得到最优属性下标
    best_attribute_label=labels[best_attribute] #最优属性标签
    tree={best_attribute_label:{}}  #生成决策树
    del(labels[best_attribute]) #删除已使用的标签(在split函数中会删除该标签属性列,这里要同步删除标签,才能对应上)

    best_attribute_values=set()  #初始化训练集中所有最优属性值
    for x in dataset:
        best_attribute_values.add(x[best_attribute])
    
    for value in best_attribute_values:
        subLabels=labels.copy()
        tree[best_attribute_label][value] = create_tree(split(dataset, best_attribute, value), subLabels)

    return tree

def classify(tree,data):   #使用决策树进行分类,data为字典类型数据,key为lable,value为值
    decision=tree.copy()    #获取tree的拷贝,以免后面的操作改变tree
    while isinstance(decision,type({})):    #decision是随着决策逐步改变,直到decision不是字典的时候才停止
        label=list(decision.keys())[0]  #获取decision的第一个键
        decision=decision[label][data[label]]   #decision的第一个键对应的值将又是一个字典或一个字符串,如果是字典,那么这个字典的键是属性值,值是一个字典或字符串
    
    return decision #最后decision将是类别


dataset,labels=createDataSet()
tree=create_tree(dataset,labels)
print("决策树为:")
print(tree)

print()

test={'Outlook':'0','Temperature':'1','Humidity':'1','Windy':'1'}
c=classify(tree,test) #no
print("{} 数据属于的类别为:".format(test))
print(c)