数据挖掘C4.5算法实现

1实验目的:

实现c4.5算法。要求:

1.给定一个训练数据集,得到决策树并用图形显示该决策树。

2. 用学习得到的决策树对测试集进行判决或分类,得到测试集上的分类错误率。

2实验过程:

  1. 数据集

     本实验采用西瓜数据集2.0

dataSet=[['青绿','蜷缩','浊响','清晰','凹陷','硬滑','是'],
             ['乌黑','蜷缩','沉闷','清晰','凹陷','硬滑','是'],
             ['乌黑','蜷缩','浊响','清晰','凹陷','硬滑','是'],
             ['青绿','蜷缩','沉闷','清晰','凹陷','硬滑','是'],
             ['浅白','蜷缩','浊响','清晰','凹陷','硬滑','是'],
             ['青绿','稍蜷','浊响','清晰','稍凹','软粘','是'],
             ['乌黑','稍蜷','浊响','稍糊','稍凹','软粘','是'],
             ['乌黑','稍蜷','浊响','清晰','稍凹','硬滑','是'],
             ['乌黑','稍蜷','沉闷','稍糊','稍凹','硬滑','否'],
             ['青绿','硬挺','清脆','清晰','平坦','软粘','否'],
             ['浅白','硬挺','清脆','模糊','平坦','硬滑','否'],
             ['浅白','蜷缩','浊响','模糊','平坦','软粘','否'],
             ['青绿','稍蜷','浊响','稍糊','凹陷','硬滑','否'],
             ['浅白','稍蜷','沉闷','稍糊','凹陷','硬滑','否'],
             ['乌黑','稍蜷','浊响','清晰','稍凹','软粘','否'],
             ['浅白','蜷缩','浊响','模糊','平坦','硬滑','否'],
             ['青绿','蜷缩','沉闷','稍糊','稍凹','硬滑','否']]

labels=['色泽','根蒂','敲声','纹理','脐部','触感']

  2.C4.5算法实现

   

2.1定义函数:计算信息熵

# 函数说明:计算给定数据集的经验熵(香农熵)
def calcShannonEnt(dataSet):
    numEntires = len(dataSet)                       #返回数据集的行数
    labelCounts = {}                               #保存每个标签(Label)出现次数的字典
    for featVec in dataSet:                        #对每组特征向量进行统计
        currentLabel = featVec[-1]                 #提取标签(Label)信息
        if currentLabel not in labelCounts.keys(): #如果标签(Label)没有放入统计次数的字典,添加进去
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1             #Label计数
    shannonEnt = 0.0                               #经验熵(香农熵)
    for key in labelCounts:                        #计算香农熵
        prob = float(labelCounts[key]) / numEntires#选择该标签(Label)的概率
        shannonEnt -= prob * log(prob, 2)          #利用公式计算
    return shannonEnt                              #返回经验熵(香农熵)

2.2 定义函数:将数据集中某一特征所在的列去掉

def splitDataSet(dataSet, axis, value):
    retDataSet = []                                #创建返回的数据集列表
    for featVec in dataSet:                        #遍历数据集
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]        #去掉axis特征
            reducedFeatVec.extend(featVec[axis+1:])#将符合条件的添加到返回的数据集
            retDataSet.append(reducedFeatVec)
    return retDataSet                              #返回划分后的数据集

2.3定义函数:计算信息增益率,并返回当前数据集的信息增益率最大的属性

def chooseBestFeatureToSplit(dataSet,label):
    numFeatures = len(label)                  #特征数量
    baseEntropy = calcShannonEnt(dataSet)                 #计算数据集的香农熵
    bestInfoGainRatio = 0.0                                    #信息增益率
    bestFeature = -1                                      #最优特征的索引值
    
    for i in range(numFeatures):                          #遍历所有特征
        #获取dataSet的第i个所有特征
        featList = [example[i] for example in dataSet]
        uniqueVals = set(featList)                         #创建set集合{},元素不可重复
        newEntropy = 0.0                                   #经验条件熵
        IV = 1e-5
        for value in uniqueVals:                           #计算信息增益
            subDataSet = splitDataSet(dataSet, i, value)   #subDataSet划分后的子集
            prob = len(subDataSet) / float(len(dataSet))   #计算子集的概率
            newEntropy += prob * calcShannonEnt(subDataSet)#根据公式计算经验条件熵
            IV -= prob * log(prob,2)                       #计算IV
        infoGain = baseEntropy - newEntropy                #信息增益
        Gain_ratio = infoGain/IV                           #增益率
        # print("第%d个特征的增益为%.3f" % (i, infoGain))   #打印每个特征的信息增益
        if (Gain_ratio > bestInfoGainRatio):                      #计算信息增益
            bestInfoGainRatio = Gain_ratio                        #更新信息增益,找到最大的信息增益
            bestFeature = i                                #记录信息增益最大的特征的索引值
    return bestFeature                                     #返回信息增益最大的特征的索引值

2.4定义函数:统计分类数组中类别最多的标签

def majorityCnt(classList):
    classCount = {}
    for vote in classList:#统计classList中每个元素出现的次数
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)#根据字典的值降序排序
    return sortedClassCount[0][0]#返回classList中出现次数最多的元素

2.5定义函数:判断所有样本在所有属性上取值是否相同

def isSame(dataSet):
    temp = dataSet[0]
    for data in dataSet:
        i =0
        while i < len(dataSet[0]):
            if temp[i] != data[i]:
                return 0
            i = i+1
    return 1

2.6定义函数:创建决策树

def createTree(dataSet,label,featLabels,G,parentId,edge_name,dataSet_orgin,label_orgin):
    label2 = label[:]
    nowId = G.number_of_nodes()+1                          #记录递归栈中本层的节点编号
    classList = [example[-1] for example in dataSet]       #取分类标签(是否放贷:yes or no)
    if classList.count(classList[0]) == len(classList):    #如果类别完全相同则停止继续划分case1
        print("nowId1==",nowId,"parentId1==",parentId)
        G.add_node(nowId,label=classList[0],fontname="Microsoft YaHei", style="filled",color="#B4E7B7")
        print("edge_name1==",edge_name)
        G.add_edge(parentId,nowId,color="#B4DBFF", penwidth=1.5, fontsize=12,fontname="Microsoft YaHei", label=edge_name)
        return classList[0]
    
    if (len(label) == 0 or isSame(dataSet)):                               #case2:当前属性集为空或者所有样本在所有属性上取值相同,返回出现次数最多的类标签
        maxLabel = majorityCnt(classList)
        G.add_node(nowId,label=maxLabel,fontname="Microsoft YaHei", style="filled",color="#dddddd")
        G.add_edge(parentId,nowId,color="#B4DBFF", penwidth=1.5, fontsize=12,fontname="Microsoft YaHei", label=edge_name)
        return maxLabel
    bestFeat = chooseBestFeatureToSplit(dataSet,label)           #选择最优特征
    print('bestFeat==',bestFeat,'labels==',labels)
    bestFeatLabel = label[bestFeat]                       #最优特征的标签
    #先把这个最优特征的点加到图中
    print("nowId2==",nowId,"parentId2==",parentId)
    G.add_node(nowId,label=bestFeatLabel,fontname="Microsoft YaHei", style="filled",color="#1296db")
    if parentId != -1:
            print('edgename2==',edge_name)
            print("nowId3==",nowId,"parentId3==",parentId)
            G.add_edge(parentId,nowId,color="#B4DBFF", penwidth=1.5, fontsize=12,fontname="Microsoft YaHei", label=edge_name)
    print("bestFeatLabel",bestFeatLabel)
    
    featLabels.append(bestFeatLabel)
    myTree = {bestFeatLabel:{}}                            #根据最优特征的标签生成树
    del(label2[bestFeat])                                  #删除已经使用特征标签
    orginIndex = label_orgin.index(bestFeatLabel)                      #输出最优特征在原始数据集的位置
    featValues = [example[orginIndex] for example in dataSet_orgin]#得到训练集中所有最优特征的属性值
    uniqueVals = set(featValues)                           #去掉重复的属性值
    print("uniqueVals",uniqueVals)
    
    fatherDataSet = []
    for value in uniqueVals:                               #遍历特征,创建决策树。
        newDataSet = splitDataSet(dataSet, bestFeat, value)
        if newDataSet:
            for item in newDataSet:
                fatherDataSet.append(item)
       
    for value in uniqueVals:                               #遍历特征,创建决策树。
        print("value==",value)
        newDataSet = splitDataSet(dataSet, bestFeat, value)
        if newDataSet:
            #print('newDataSet==',newDataSet)
            myTree[bestFeatLabel][value] = createTree(newDataSet,label2, featLabels,G,nowId,value,dataSet_orgin,label_orgin)
        else:
            #print('fatherDataSet==',fatherDataSet)
            classList = [example[-1] for example in fatherDataSet]
            mlabel = majorityCnt(classList)
            print("mlabel==",mlabel)
            nextId = G.number_of_nodes()+1 
            G.add_node(nextId,label=mlabel,fontname="Microsoft YaHei", style="filled",color="#B4E7B7")
            G.add_edge(nowId,nextId,color="#B4DBFF", penwidth=1.5, fontsize=12,fontname="Microsoft YaHei", label=value)
    return myTree

2.7定义函数:测试模型

def judge(node,data,labels):
    key=list(node.keys())[0]
    node = node[key]
    idx = labels.index(key)
    pred = None
    for key in node:
        if data[idx] == key:
            if isinstance(node[key],dict):
                pred = judge(node[key],data,labels)
            else:
                pred = node[key]
            
    return pred

2.8划分数据集训练模型

G = pgh.AGraph()
#划分数据集
np.random.shuffle(readyArr)
offset = int(len(readyArr)*0.9)
trainData = readyArr[:offset]
testData = readyArr[offset:]
featLabels = []
Trees = createTree(trainData,labels,featLabels,G,-1,'0',trainData,labels)
print(Trees)
G.layout()
G.draw(r'C:\Users\admin\OneDrive\anaconda\c45test.dot')

2.9测试模型,并计算错误率

errSum =0
for data in testData:
    test_result = judge(Trees,data,labels)
    if test_result != data[-1]:
        errSum = errSum +1
totalNum = len(testData)
print("出错率为",float(errSum/totalNum))

3实验总结:

  1. 在决策树生成过程中, 由于为了方便计算决策树分支的最优属性,所以会删除已经作为过最优属性的属性,当某个属性被用到后直接删除后,在后面再使用到该属性时会出现错误。因此,需要改正算法,思路是在递归时,只有在进入分类属性X下一层递归时才会删除当前分类属性X,当前分类属性X平行的节点则不会删除分类属性X.
  2. 分类到某分支时,会出现某属性A的某个取值v没有样本数据的情况,需要将该属性A当前样本的最多的分类作为v的分类。
  3. 另外,也采用sklearn进行训练,对比C4.5算法结果,发现不一样。通过阅读源码,得知sklearn 里面的决策树采用的是基尼指数,而C4.5决策树采用的是信息增益率来选择最优分类属性。

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值