zoukankan      html  css  js  c++  java
  • 决策树 ID3 实践

     一、实战文件

    色泽 根蒂 敲声 纹理 脐部 触感 好瓜
    青绿 蜷缩 浊响 清晰 凹陷 硬滑 是
    乌黑 蜷缩 沉闷 清晰 凹陷 硬滑 是
    乌黑 蜷缩 浊响 清晰 凹陷 硬滑 是
    青绿 蜷缩 沉闷 清晰 凹陷 硬滑 是
    浅白 蜷缩 浊响 清晰 凹陷 硬滑 是
    青绿 稍蜷 浊响 清晰 稍凹 软粘 是
    乌黑 稍蜷 浊响 稍糊 稍凹 软粘 是
    乌黑 稍蜷 浊响 清晰 稍凹 硬滑 是
    乌黑 稍蜷 沉闷 稍糊 稍凹 硬滑 否
    青绿 硬挺 清脆 清晰 平坦 软粘 否
    浅白 硬挺 清脆 模糊 稍凹 硬滑 否
    浅白 蜷缩 浊响 模糊 稍凹 软粘 否
    青绿 稍蜷 浊响 稍糊 稍凹 硬滑 否
    浅白 稍蜷 沉闷 稍糊 稍凹 硬滑 否
    乌黑 稍蜷 浊响 清晰 稍凹 软粘 否
    浅白 蜷缩 浊响 模糊 稍凹 硬滑 否
    青绿 蜷缩 沉闷 稍糊 稍凹 硬滑 否

    二、实战代码

      1 # -*- coding: utf-8 -*-
      2 """
      3 Created on Sat Sep 22 19:48:18 2018
      4 
      5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      6 !!!!!      
      7 !!!!!          
      8           ID3 algorithm  without data missing or error          
      9           Discontinuous value                                                        
     10                                                                    !!!!!
     11                                                                    !!!!!
     12 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     13 
     14 @author: Administrator
     15 """
     16 
     17 import math;
     18 import pandas as pd;
     19 import pickle;
     20 #获取数据集 返回DataFrame对象  列名为特征名 最后一列为不同类别
     21 def getData():                              
     22     with open('MelonData.txt','r') as f:
     23         y=f.read()
     24     y=y.split('
    ')
     25     x=[];
     26     flag=0;
     27     for i in y:
     28         if flag==0:
     29             title=i.split(' ');
     30             flag=1
     31         else:
     32             x.append(i.split(' '))
     33     y=pd.DataFrame(x,columns=title);
     34     return y
     35 
     36 
     37 #留出法 每一类别1:4 开   将样本分为测试样本和训练样本   模型评估
     38 def divSamples(data):
     39     num={};                                       #不同类别数量
     40     cnt={};                                       #分类
     41     kinds=set( list(data[data.columns[-1] ]) );   #获取类别
     42     kinds=list(kinds);                            
     43     
     44     for i in kinds:                               #不同类别初始化        
     45         num[i]=0;
     46         cnt[i]=0;
     47     for i in data[ data.columns[-1] ]:            #统计不同类别数量  
     48         num[ i ]=num[ i ]+1
     49     trainingSets=data.copy();                     #复制一份训练集
     50     testingSets=data.copy();
     51     for i in range(0,len(data)):
     52         if(  cnt[ data[ data.columns[-1] ][i] ] >= num[  data[ data.columns[-1] ][i] ]*4//5  ):
     53             trainingSets=trainingSets.drop(i)
     54         else:
     55             testingSets=testingSets.drop(i)
     56             cnt[ data[ data.columns[-1] ][i] ]+=1
     57     trainingSets.index=list(range(0,len(trainingSets)) );   #重新行索引
     58     testingSets.index=list(range(0,len(testingSets))) ;
     59 
     60     return trainingSets,testingSets
     61     
     62 #计算所有特征下的信息增益
     63 def getFeatureEntropy(data):
     64     num={};                                       #不同类别数量  
     65     kEntropy=0                        #   每一类别的熵
     66     kPoss={}                            #   每一类别的概率( 频率 )
     67     feaEntropy={}                       #   已知每个特征的熵
     68     feaPoss={}                          #   每类特征概率
     69     feaNum={}                           #   统计每个特征不同特征值的不同类别的数量
     70     feaNumAll={}                        #   统计每个特征不同特征值的数量
     71  
     72     #不同类别的熵
     73     kinds=set( list(data[data.columns[-1] ]) );   #获取类别
     74     kinds=list(kinds);           
     75     
     76     for i in kinds:                               #不同类别初始化        
     77         num[i]=0;
     78     for i in data[ data.columns[-1] ]:            #统计不同类别数量  
     79         num[ i ]=num[ i ]+1
     80     
     81     for i in num:  
     82         kPoss[i]=num[i]/len(data);                 #  k类型概率
     83         if(kPoss[i]!=0):
     84             kEntropy+=(-kPoss[i]*math.log2(kPoss[i])); #  k类型的entropy  // log以2为底
     85                                                      
     86     #   计算在每个特征下的熵
     87     
     88     for i in range(0,len(data.columns)-1):         #   初始化feaNum,feaNumAll
     89         feaNumAll[data.columns[i]]={};              
     90         feaNum[data.columns[i]]={}   ;             #   该特征下每个特征值
     91         feaPoss[data.columns[i]]={} ;
     92                
     93         fea=set( data[data.columns[i]] );         
     94         for j in fea:                              #   同一特征值的样本数
     95             feaNumAll[data.columns[i]][j]=0;
     96             feaNum[data.columns[i]][j]={}
     97             for k in kinds:                        #   同一特征值下每个类别的样本数量
     98                 feaNum[data.columns[i]][j][k]=0;    
     99 
    100     for i in range(0,len(data.columns)-1):         #   计算在每个特征下的样本数量
    101         fea=data[data.columns[i]] ;
    102         label=data[data.columns[-1]];
    103         for j in range(0,len(fea)):                         #   每个特征值下
    104             feaNum[data.columns[i]][fea[j]][label[j]]+=1 ;  #   每个类别的量
    105             feaNumAll[data.columns[i]][fea[j]] +=1;
    106      
    107     for i in feaNum:                                        #计算不同特征下不同特征值的概率 条件熵       
    108         feaPoss[i]={}
    109         for j in feaNum[i]:
    110             feaPoss[i][j]={}
    111             for k in feaNum[i][j]:
    112                 feaPoss[i][j][k]=feaNum[i][j][k]/feaNumAll[i][j];
    113     
    114     for i in feaNumAll:
    115         en=0;                          #每个特征下的熵     
    116         for j in feaNum[i]:
    117             en1=0;
    118             if( feaNumAll[i][j]!=0 ):
    119                 for k in feaNum[i][j]:
    120                     if( feaNum[i][j][k]!=0 ):
    121                         en1+=(-feaPoss[i][j][k] *math.log2( feaPoss[i][j][k]) )
    122             en+=en1*feaNumAll[i][j]/len(data);
    123         feaEntropy[i]=kEntropy-en; 
    124     
    125     return  feaEntropy;                      #计算信息每个特征的增益字典
    126 
    127 
    128 #划分数据集
    129 def splitDataSet(data,column,value):    #划分某一列某一属性值
    130     newSet=data.copy();                 #复制新的数据集
    131     for i in range(0,len(data)):
    132         if( data[column][i]!=value ):
    133             newSet=newSet.drop(i);
    134     newSet=newSet.drop(columns=column);
    135     newSet.index=list(range(0,len(newSet)));
    136     return newSet
    137 
    138 #预剪枝 判断是否要划分  根据划分与不划分的错误率 
    139 def prePruning(data,column):      #   data为测试数据
    140     label=set(data[ data.columns[-1] ] );
    141     lab={};
    142     for i in label:             #每一类别数量
    143         lab[i]=0;
    144    
    145     incorNum=0;                 #不划分时错误的数量
    146     for i in data[ data.columns[-1] ] :
    147         lab[i]+=1;
    148     val=0
    149     for i in lab:                            
    150         if(val<lab[i]):
    151             feaNum=lab[i]
    152             NotDiv=i;            #不划分时的类别
    153             val=lab[i];
    154     incorNum=len(data)-feaNum;   
    155                                    
    156     feaVal=set(data[column]);   #计算划分时的错误数量
    157     fea={}                      #特征值集合
    158     for i in feaVal:            #初始化
    159         fea[i]={};              
    160         for j in label:
    161             fea[i][j]=0;
    162     
    163     for i in range( 0,len(data) ):         
    164         fea[ data[column][i]  ][ data[data.columns[-1] ][i] ]+=1;
    165     
    166     divNum=0;                   #  划分时错误的数量
    167     for i in fea:     #每特征的类别数量
    168         val=0;
    169         allNum=0
    170         for j in fea[i]:
    171             if(fea[i][j]>val):
    172                 val=fea[i][j];
    173             allNum+=fea[i][j]
    174         divNum+=(allNum-val);  
    175     if(divNum<=incorNum):
    176         return True,NotDiv      ###要划分,NotDiv无意义
    177     else:
    178         return False,NotDiv;    ##不要划分,NotDiv表示类别
    179 
    180 #后剪枝
    181 def postPruning(testingSet,tree):   # testingSet 测试数据   递归  
    182     for i in tree:
    183         for j in tree[i]:
    184             if( type(tree[i][j])==type({}) ):
    185                 newSet=splitDataSet(testingSet,i,j);
    186                 tree[i][j]=postPruning(newSet,tree[i][j]);   
    187     IsDiv,label=prePruning(testingSet,list(tree)[0]);    #类似预剪枝  所以,用了prePruning 就不必用postPruning 本代码为调用postPruning函数
    188     if(IsDiv):
    189         return label;
    190     else:
    191         return tree;        
    192                 
    193         
    194 #建树
    195 def createTree(data):
    196     label=set(data[ data.columns[-1] ]);
    197     if(len(label)==1):
    198         return data[ data.columns[-1] ][0]   #如果类别相同则返回该类别
    199     tree={};
    200     feaEntr=getFeatureEntropy(data)   #获取各特征信息增益
    201     val=0
    202     for i in feaEntr:
    203         if(feaEntr[i]>val):
    204             val=feaEntr[i]
    205             fea=i
    206    
    207     IsDiv,NotLabel=prePruning(data,fea);       #预剪枝 判断是否要划分
    208     if( feaEntr[fea] > 0.01 and IsDiv ):             #预剪枝
    209         tree[fea]={}
    210         feaVal=set(data[fea])             #获取不同特征值
    211         for i in feaVal:    
    212             newSet=splitDataSet(data,fea,i)
    213             tree[fea][i]=createTree(newSet);
    214     else:
    215         tree[fea][i]=NotLabel
    216     return tree;
    217 
    218 #模型评价  返回查准率
    219 def modelEvaluation( testingSet,tree ):
    220     corrNum=0;
    221     testingSet=classify(tree,testingSet);
    222     for i in range(0,len(testingSet)):
    223         if(testingSet['好瓜'][i]==testingSet['预测'][i]):
    224             corrNum+=1
    225     return corrNum/len(testingSet)
    226 
    227 #将新的数据分类
    228 def classify(tree,data):      # data 为 DataFrame 类型
    229     x=[];
    230     for i in range(0,len(data)):
    231         tt=tree;
    232         while(True):
    233             fea=list(tt)[0];
    234             if fea in data.columns:
    235                 tt=tt[fea][data[fea][i]]
    236             else:
    237                 x.append(fea);
    238                 break;
    239     newData=data.copy();
    240     newData.insert(len(data.columns),'预测',x)
    241     return newData
    242 #将决策树序列化        
    243 def saveTree(tree):
    244     with open('TreeSerializaiton.txt','wb') as f:   #pickle库序列化
    245         pickle.dump(tree,f);   
    246 
    247 def main():
    248     xx=getData();
    249     trainingSet,testingSet=divSamples(xx);   ##将数据分为训练集,测试集
    250     trainingSet=testingSet=xx;               ##此处由于样本数少,不进行划分
    251     tree=createTree(trainingSet);            ##创建决策树
    252     postPruning(testingSet,tree)             ## 后剪枝 ##剪了和没剪枝一个样,
    253     rate=modelEvaluation( testingSet,tree )  ##模型评价 查准率
    254     
    255 if __name__== '__main__':
    256     main()
  • 相关阅读:
    移动网页如何只调出数字键盘
    把HTML5网页封装成APP,APK的方法
    HTML5,微信开发原码社区
    display:table 水平居中
    input在苹果浏览器下变成圆角的解决方案
    四种方法解决DIV高度自适应问题
    jquery prop和attr的区别
    移动端网页JS框架-手机触摸事件框架,日历框架带滑动效果
    meta viewport标签的使用说明(手机浏览缩放控制)
    javascript json格式解析方法
  • 原文地址:https://www.cnblogs.com/z-bear/p/9700864.html
Copyright © 2011-2022 走看看