手敲决策树---ID3代码

学习算法的最好方式,莫过于亲手敲,程序跑一遍。也许代码不精炼,也许比较费时。但学习这事情,如果怕麻烦还怎么学的好。

本次手敲的代码是按照信息增益的大小,来决定决策树的分支。这里先说几个关键的点:

1.决策树的每个分支都要用到上一步的计算结果,故采用递归的循环方式。

2.要注意递归的终止条件。一是分支的数据里面就只有一种分类,比如二分类的话,返回的结果要么全为是,要么全为否,就需要终止了。二是所有的特征都跑完了,最后根据特征的具体取值,观察对应二分类的每个类别的个数,哪个类别的个数多,则特征具体取值对应的结果就是这个类别(见下图,仅为举例说明)。

3.决策树中的很多步骤都需要计算频率,频率的计算方法比较多,本次这里面主要用的是pandas的聚合函数,pd.crosstab(),pd.groupby(),等等。在根据特征的每个取值拆分数据集的时候,用到了groupby自动拆分的方法。(参见《利用python进行数据分析》P267页。)

 4.本次使用的数据,是周志华《机器学习》中的内容。数据来源于https://blog.csdn.net/icefire_tyh/article/details/54575527

遍历本人还不是太熟,部分参考了https://blog.csdn.net/herosofearth/article/details/52347820#comments

上代码:

#初始熵的计算
def initi_entro(data):
    pk=data.iloc[:,-1].value_counts()/data.iloc[:,-1].count()
    entro2=-(pk[0]*math.log(pk[0],2)+pk[1]*math.log(pk[1],2))
    return entro2
#熵的计算公式
def entro_compute(data):
    m=-(data*math.log(data,2))
    return m

#用于计算并返回数据中信息增益最大的变量.(变量等价于特征)
def entro_compute2(data):     
    l=len(data.keys())-1
    global gg#用于存放变量名和该变量名称对应的熵值
    gg={}
    for i in range(l):#循环用于计算每个变量的熵
        print('选取变量:',data.keys()[i])
        G=0#用于存放熵值
        #建立每个变量与预测变量的交叉表,即形成频数表。
        column1=pd.crosstab(data.iloc[:,i],data.iloc[:,-1],margins=True)
        data1=column1.iloc[:,0]/column1.iloc[:,-1]
        data2=column1.iloc[:,1]/column1.iloc[:,-1]
        m=len(data.iloc[:,i].unique())
        for j in range(m):
            #如果某一取值对应的某一结果分类的频数为0,则该取值对应的结果分类的熵值为0.
            if data1[j]==0:#x 表示变量的某一取值对应的所有结果的熵。
                x=0+entro_compute(data2[j])
            elif data2[j]==0:
                x=0+entro_compute(data1[j]) 
            else:
                #计算每个类别下,取各个值对应的熵
                x=entro_compute(data1[j])+entro_compute(data2[j])
            #每个类别的权重
            y=column1.iloc[j,-1]/column1.iloc[-1,-1]
            G+=x*y
        ll=initi_entro(data)-G#变量的信息增益
        gg[data.keys()[i]]=ll#存储到gg中
        print(data.keys()[i],'分类情况下的熵为','%.3f'%ll)    
    #找出信息增益最大的分类变量的取值
    s=sorted(gg.items(),key=lambda x:x[1],reverse=True)
    return s[0][0]
def splitdata(data,features):
    subdata={}
    for x,y in data.groupby(features):
        dataa=(x,y)#生成拆分的数据
        subdata[dataa[0]]=dataa[1]
    return subdata
def mostfrequency(data):
    frequentvar=data.iloc[:,-1].value_counts(ascending=False).index[0]
    return  frequentvar
def createTree(data,labels):
    '''
    创建决策树
    :data:数据集
    :labels:所有变量/特征标签
    '''
    if len(data.iloc[:,-1].unique())==1:
        return data.iloc[0,-1]        #第一个递归条件结束,值相同,返回最后一列的第一个值。
    if len(labels) == 1:        
        return  mostfrequency(data)   # 第二个递归结束条件:用完了所有特征,返回频率最高的值
    bestFeat=entro_compute2(data)     # 最优划分特征,返回的是最有的特征
    myTree = {bestFeat:{}}       # 使用字典类型储存树的信息
    labels.remove(bestFeat)      # 删除最优的特征
    uniqueVals=data.loc[:,bestFeat].unique()
    subdata=splitdata(data,bestFeat)
    for value in uniqueVals:
        subLabels = labels[:]       # 复制所有类标签,保证每次递归调用时不改变原始列表的内容
        myTree[bestFeat][value] = createTree(subdata[value],subLabels)
        #myTree[bestFeat][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree

运行并读取数据(由于使用的原始数据集最后一列名称有个空格,故修改了变量名。你也可以直接strip 去掉空格)

data=pd.read_csv('D:\\Users\\Administrator\\Documents\\decisiontreedata.csv',encoding='GB2312',index_col=0)
data.rename(columns={'好瓜  ':'好瓜'},inplace=True)
#整理成最后一列是分类变量的形式
data=data[['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '好瓜']]
#计算初始的信息熵
labels=list(data.keys())
createTree(data,labels)

运行结果:

{'纹理': {'模糊': '否  ',
  '清晰': {'根蒂': {'硬挺': '否  ',
    '稍蜷': {'色泽': {'乌黑': {'触感': {'硬滑': '是  ', '软粘': '否  '}}, '青绿': '是  '}},
    '蜷缩': '是  '}},
  '稍糊': {'触感': {'硬滑': '否  ', '软粘': '是  '}}}}

 与《机器学习》P78页图4.4对照,完全一致。附:原始数据集

	色泽	根蒂	敲声	纹理	脐部	触感	好瓜
编号							
1	青绿	蜷缩	浊响	清晰	凹陷	硬滑	是
2	乌黑	蜷缩	沉闷	清晰	凹陷	硬滑	是
3	乌黑	蜷缩	浊响	清晰	凹陷	硬滑	是
4	青绿	蜷缩	沉闷	清晰	凹陷	硬滑	是
5	浅白	蜷缩	浊响	清晰	凹陷	硬滑	是
6	青绿	稍蜷	浊响	清晰	稍凹	软粘	是
7	乌黑	稍蜷	浊响	稍糊	稍凹	软粘	是
8	乌黑	稍蜷	浊响	清晰	稍凹	硬滑	是
9	乌黑	稍蜷	沉闷	稍糊	稍凹	硬滑	否
10	青绿	硬挺	清脆	清晰	平坦	软粘	否
11	浅白	硬挺	清脆	模糊	平坦	硬滑	否
12	浅白	蜷缩	浊响	模糊	平坦	软粘	否
13	青绿	稍蜷	浊响	稍糊	凹陷	硬滑	否
14	浅白	稍蜷	沉闷	稍糊	凹陷	硬滑	否
15	乌黑	稍蜷	浊响	清晰	稍凹	软粘	否
16	浅白	蜷缩	浊响	模糊	平坦	硬滑	否
17	青绿	蜷缩	沉闷	稍糊	稍凹	硬滑	否

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值