决策树 id3 C++实现

程序目的:根据一定数量的训练样本,生成决策树。依靠生成的决策树测试预测样本的分类。

代码:

简单版:

http://download.youkuaiyun.com/detail/hzq20081121107/9251673

升级版:

http://download.youkuaiyun.com/detail/hzq20081121107/6481505


参考资料:http://download.youkuaiyun.com/detail/hzq20081121107/7829801

训练样本:


其中前4个属性为特征属性,buys-computer为分类属性.

程序读入的是txt文件,data_train.txt:


age income student credit_rating buys_computer
youth high no fair no
youth high no excellent no
middle_aged high no fair yes
senior medium no fair yes
senior low yes fair yes
senior low yes excellent no
middle_aged low yes excellent yes
youth medium no fair no
youth low yes fair yes
senior medium yes fair yes
youth medium yes excellent yes
middle_aged medium no excellent yes
middle_aged high yes fair yes
senior medium no excellent no

程序将该数据转换成数值类型,程序读入train_data.txt文件后,生成转换关系:


程序根据转换关系转换train_data.txt的字符型数据成为数字型数据:


训练样本转换成数值类型后,根据数值类型的训练样本 采用id3算法 递归生成决策树:


以上决策树是根据程序得到的结点信息用excel表画出来的,程序生成的实际的结点信息在 tree2.txt文件中:


程序对测试样本做检测(自洽检验),准确率为100%。


本程序有以下优点:

1.程序具有良好的通用性。

不需要对样本字符串数据做转化,自动根据样本中出现的数据生成 转换关系并 转换数据再生成决策树。

2.程序虽然处理的文件为txt文件,但是可以导入到excel表格中,进而再导入到数据库中。数据库中的文件同样可以逆序导出到txt文件中。

3.程序对语音合成技术中的韵律数据做过测试。本程序用3769条数据进行了训练,用370条数据做预测,预测的准确率为98.6%。

以下为C++程序源码,(环境为dev C++):

代码:

#include<iostream>
#include<fstream>
#include<cmath>
#include<map>
#include<list>
using namespace std;
//宏 
#define Nfeature 100            //特征数(feature)+1的上限 
#define Nsample  7000           //样本数量的 上限 
#define Nfeaturechoice 50       //属性 取值种类 的上限 

//结构体
struct featureNode              //特征属性信息的结构体 
{
   string name;                 //属性的名称 
   int order;                   //属性所对应的序号,按出现顺序 
   int num;                     //属性取值的种类 
   string strV[Nfeaturechoice]; //属性标号对应属性名称int->string  0->age 
   map<string,int> m;           //属性名称对应属性标号string->int  age->0
};
struct strNode                  //样本信息的结构体  
{
    int num;                    //样本的标号,按出现顺序 
    string strV[Nfeature];      //属性取值标号对应属性取值名称int->string  age: 0->middle
    int numV[Nfeature];         //属性取值名称对应属性取值标号string->int  age: middle->0
};  
struct searchNode               //搜索节点的结构体  
{
   int numFleft;                //为划分的属性的数量 
   int orderFchoose;            //当前节点往下分支采用的属性的序号  age 0 
   int orderClass;              //当前节点中数量最多的某类样本的类别标号 middle 0 
   int fchoose[Nfeature];       //当前节点使用过的属性 数组标记 
   int featurePath[Nfeature];   //访问当前节点所经历分支属性的序号   组成的序列 age credit_rating :0 3 
   int fvaluePath[Nfeature];    //访问当前节点所经历分株属性的值选择 组成的序列 senior excellent:1 0 
   list <strNode> convL;        //当前节点包含的样本 
   map <int,searchNode> childL; //当前节点的分支  
};

//全局变量 
featureNode fNode[Nfeature];    //每个属性(字段)对应一个fNode【i】 
strNode oriNode[Nsample];       //每个样本对应一个oriNode【i】 
string feature[Nfeature];       //属性名称数列,feature【i】表示出现的第i个属性的名称 
searchNode Root;                //决策树的树根  
int numFeature;                 //feature 的数量+1(类别) 
int numSample;                  //样本的数量 

//全局函数 
void id3_input();                                            //从文本文件"data_train.txt"接受字符串信息,转化成数字信息并生成词典 
void id3_analysis();                                         //分析 初始化决策树树根,并构建决策树 
void id3_buildTreeDFS(searchNode &croot);                    //深度搜索构建决策树,父节点为croot
float id3_culGain(list<strNode> convList,int orderChoose);   //计算convList链表中的数据集 按照第coderChoose个属性划分所得的信息增益 
void id3_show();                                             //输出决策树 
void id3_showTree_dfs(searchNode cRoot);                     //递归显示决策树以search为根的所有节点  
void id3_show_searchNode2(searchNode sn);                    //以追加的方式显示一个决策节点sn的信息到文件"tree2.txt"   
void id3_test();                                             //从文件"data_test.txt"中读取测试样本,并作测试 
int id3_classify(strNode strN,searchNode croot);             //从节点croot开始决策样本strN,返回该样本的决策分类号 

//函数实现: 
//从文本文件"data_train.txt"接受字符串信息,转化成数字信息并生成词典 
void id3_input()
{
    ifstream cin("data_train.txt");//train_samples.txt");
    char strField[1024];
    cin.getline(strField,1024);
    int len=strlen(strField);
    int i,j,k;
    //处理第一行,为字段名 
    string str;
    for(i=0,j=0;i<len;i++)
    {
        if(strField[i]!=' ')
        {
            str+=strField[i];                  
        }       
        else
        {
            feature[j]=str;
            j++;
            str="";    
        }
    }
    feature[j]=str;
    numFeature=j+1;
    
    for(i=0;i<numFeature;i++)
    {
        fNode[i].name=feature[i];
        fNode[i].order=i;
    }
    //接受样本数据(字符串)到oriNode    
    i=j=0;    
    while(cin>>oriNode[i].strV[0])
    {
        for(j=1;j<numFeature;j++)
            cin>>oriNode[i].strV[j];  
        oriNode[i].num=i;
        i++;                           
    }    
    cin.close();
    numSample=i;
    //转换各字段的 各string属性-》int  及  int-》string;求得fNode字段结构体的m和strV 
    for(i=0;i<numSample;i++)
    {
        for(j=0;j<numFeature;j++)
        {
            fNode[j].m[oriNode[i].strV[j]];                         
        }                        
    }
    map<string,int>::iterator it;
    for(i=0;i<numFeature;i++)
    {
        fNode[i].num=fNode[i].m.size();
        for(j=0,it=fNode[i].m.begin();it!=fNode[i].m.end();it++,j++)
        {
            fNode[i].m[(*it).first]=j;
            fNode[i].strV[j]=(*it).first;                                                
        }                        
    }
    //oriNode属性的string-》int转换 
    for(i=0;i<numSample;i++)
    {
        for(j=0;j<numFeature;j++)
        {
            oriNode[i].numV[j]=fNode[j].m[oriNode[i].strV[j]];                         
        }                        
    }
    
    ofstream cout("input_convertion.txt");
    //显示收集到的fNode字段结构体的信息        
    for(i=0;i<numFeature;i++)
    {
        cout<<"属性标号:"<<fNode[i].order<<"; 属性名称:"<<fNode[i].name<<"; 属性取值种类:"<<fNode[i].num<<endl;
        for(j=0,it=fNode[i].m.begin();it!=fNode[i].m.end();it++,j++)
        {
            cout<<(*it).second<<" "<<(*it).first<<"  ";                                              
        } 
        cout<<endl<<endl;                       
    }  /**/
    //显示转换后的样本表的信息 
    cout<<"转换后的样本表的信息"<<endl;
    for(i=0;i<numFeature;i++)
        cout<<i<<" ";
    cout<<endl;
    for(i=0;i<numSample;i++)
    {
        for(j=0;j<numFeature;j++)
        {
            cout<<oriNode[i].numV[j]<<" ";                         
        }
        cout<<endl;
    } 
    cout.close();
    return ;
}
//以追加的方式显示一个决策节点sn的信息到文件"tree2.txt"  
void id3_show_searchNode2(searchNode sn)
{
    ofstream cout("judgeTree.txt",ios::app);
    cout<<"####################"<<endl;
    cout<<"orderClass: "<<sn.orderClass<<endl; 
    int i;
    cout<<"featurePath: ";
    for(i=0;i<numFeature-1-sn.numFleft;i++)
      cout<<sn.featurePath[i]<<" ";
    cout<<endl;      
    cout<<"fValuePath: ";
    for(i=0;i<numFeature-1-sn.numFleft;i++)
      cout<<sn.fvaluePath[i]<<" ";
    cout<<endl;      
    cout.close();
}
//计算convList链表中的数据集 按照第coderChoose个属性划分所得的信息增益 
float id3_culGain(list<strNode> convList,int orderChoose)
{
     float Eaf,gain;
     int numCsample,q,m,i,j;
     numCsample=convList.size();
     q=fNode[orderChoose].num;    //“选择属性”的取值种类 
     m=fNode[numFeature-1].num;   //“类别属性”的取值种类     
     int X[q];//=new int(q);
     for(i=0;i<q;i++)
         X[i]=0;
     int Xjs[q*m];//=new int(q*m);
     for(i=0;i<=q*m;i++)
         Xjs[i]=0;
     
     list <strNode>::iterator it;
     for(it=convList.begin();it!=convList.end();it++)
     {
         int temp1= (*it).numV[orderChoose];  //所枚举样本在“选择属性”中的取值 3
         int temp2= (*it).numV[numFeature-1]; //所枚举样本在“类别属性”中的取值 2
         X[temp1]++;
         Xjs[temp1*m+temp2]++;//选择属性为temp1,类别属性为temp2的样本数量                                                
     }   
     Eaf=0;
     for(i=0;i<q;i++)//i=选择属性的某个取值age。middle
     {
         float info=0;
         for(j=0;j<m;j++)//j=分类属性的某个取值 buy-computer。no 
         {
             float p=0;
             if(X[i]>0)
                 p=float(Xjs[i*m+j])/float(X[i]);
             if(Xjs[i*m+j]!=0)
                 info-=p*log(p)/log(2.0);           
             //printf("p=%f ",p);     
         }
         float pi=float(X[i])/float(numCsample);
         Eaf+=pi*info;                
     } 
     return 0.94-Eaf;
}
//深度搜索构建决策树,父节点为croot
void id3_buildTreeDFS(searchNode &croot)
{
     //计算croot的oderClass属性 
     int i,j,bigclass,numBigclass;
     int arrNumClass[Nfeaturechoice]={0}; 
     int filledClass=0;                       //有样本的分类个数 
     
     list<strNode>::iterator it;
     for(it=croot.convL.begin();it!=croot.convL.end();it++)
     {
         arrNumClass[(*it).numV[numFeature-1]]++;                                                            
     }
     bigclass=0;
     numBigclass=arrNumClass[0];
     for(i=0;i<fNode[numFeature-1].num;i++)
     {
         if(arrNumClass[i]>0)
             filledClass++;
         if(arrNumClass[i]>=arrNumClass[bigclass])
         {
             bigclass=i;
             numBigclass=arrNumClass[i];                                         
         }          
     }
     croot.orderClass=bigclass;                                                 //croot ********** orderClass  
     if(filledClass==1)
     {
         croot.orderFchoose=-1;              
         return;             
     }
     if(croot.numFleft==0)
         return;
     //计算croot的   orderFchose属性 
     int orderBig;
     float bigGain=-99999;
     float Gain[Nfeature]={0};//对应于划分每个属性的熵 
     for(orderBig=0,i=0;i<numFeature-1;i++)//枚举每一个feature,不包括最后分类
     {
         if(croot.fchoose[i]==0)
             continue; 
         Gain[i]=id3_culGain(croot.convL,i);
         if(i==0) bigGain=Gain[0];
         if(Gain[i]>bigGain)
         {
             bigGain=Gain[i];
             orderBig=i;                   
         } 
     } 
     croot.orderFchoose=orderBig;                                               //croot ********** orderFchoose
     croot.featurePath[numFeature-1-croot.numFleft]=orderBig;                   //croot ********** FeaturePath
     //计算croot的   childL属性 
     //计算childNode的 numFleft,fchoose【】,convL属性 
     for(i=0;i<fNode[croot.orderFchoose].num;i++)//枚举第orderBig特征的每一个属性,生成子一棵树 
     {
         searchNode childNode;
         childNode.numFleft=croot.numFleft-1;                                   //childNode ######### numFleft
         for(j=0;j<numFeature-1;j++)                                            //childNode ######### fchoose[]
         {
             childNode.fchoose[j]=croot.fchoose[j];
             if(j==croot.orderFchoose)
                 childNode.fchoose[j]=0;                           
         }
         for(j=0;j<numFeature-1-croot.numFleft;j++)                             //childNode ######### featurePath -1 FvaluePath-1
         {
             childNode.featurePath[j]=croot.featurePath[j];
             childNode.fvaluePath[j]=croot.fvaluePath[j];
         }   
         childNode.featurePath[numFeature-1-croot.numFleft]=orderBig;           //childNode ######### featurePath                                                 
         for(it=croot.convL.begin();it!=croot.convL.end();it++)                 //childNode ######### convL   
         {
             if((*it).numV[croot.orderFchoose]==i)
             {
                 childNode.convL.push_back(*it);                                
                 arrNumClass[i]++;
             }                                                    
         }
         if(childNode.convL.size()>0)//如果树中的样本数为空,不拓展该分支
         { 
             childNode.fvaluePath[numFeature-1-croot.numFleft]=i;               //childNode ######### FvaluePath
                             
             id3_buildTreeDFS(childNode); 
             croot.childL[i]=childNode;                                         //croot ********** childL                                    
         } 
     }
}
//分析 初始化决策树树根,并构建决策树 
void id3_analysis()
{
     int i;
     for(i=0;i<numSample;i++)
     {
         Root.convL.push_back(oriNode[i]);                       
     }
     Root.numFleft=numFeature-1;
     for(i=0;i<numFeature-1;i++)
         Root.fchoose[i]=1;
     id3_buildTreeDFS(Root); 
}
//递归显示决策树以search为根的所有节点  
void id3_showTree_dfs(searchNode cRoot)
{
     id3_show_searchNode2(cRoot);
     map <int,searchNode>::iterator it;
     for(it=cRoot.childL.begin();it!=cRoot.childL.end();it++)
     {
          id3_showTree_dfs((*it).second);                                                           
     }
}
//输出决策树 
void id3_show()
{
    ofstream fout("tree2.txt");
    fout<<"numFeature: "<<numFeature<<endl;
    fout.close();
    id3_showTree_dfs(Root);  
}
//从节点croot开始决策样本strN,返回该样本的决策分类号
int id3_classify(strNode strN,searchNode croot)
{
    if(croot.childL.size()==0)
        return croot.orderClass;
    map <int,searchNode>::iterator it;
    it=croot.childL.find(strN.numV[croot.orderFchoose]);
    if(it==croot.childL.end())
        return croot.orderClass;
    else
        return id3_classify(strN,(*it).second);    
}
 //从文件"data_test.txt"中读取测试样本,并作测试 
void id3_test()
{
    ifstream cin("data_test.txt");//train_samples.txt");
    int i,j,k;
    //越过首行 
    char strField[1024];
    cin.getline(strField,1024);
    //接受样本数据(字符串)到oriNode    
    i=j=0;    
    while(cin>>oriNode[i].strV[0])
    {
        for(j=1;j<numFeature;j++)
            cin>>oriNode[i].strV[j];  
        oriNode[i].num=i;
        i++;                           
    }    
    cin.close();
    numSample=i;
    //oriNode属性的string-》int转换 
    for(i=0;i<numSample;i++)
    {
        for(j=0;j<numFeature;j++)
        {
            oriNode[i].numV[j]=fNode[j].m[oriNode[i].strV[j]];                         
        }                        
    }
    ofstream cout("test_result.txt");
    int num2=0;
    int numRight=0;
    for(i=0;i<numSample;i++)
    {
         k=id3_classify(oriNode[i],Root);
         cout<<k<<endl;                   
         if(oriNode[i].numV[numFeature-1]==2)
             num2++;
         if(k==oriNode[i].numV[numFeature-1])
             numRight++; 
    }
    cout<<"numSample is "<<numSample<<endl;
    cout<<"numRight is "<<numRight<<endl;
    cout<<"right ratio is "<<float(numRight)/float(numSample)<<endl;
    cout.close();
    return ; 
}
int main()
{
    id3_input();
    printf("input is done\n");
    id3_analysis();
    printf("judge tree is built\n");
    id3_show(); 
    printf("judge tree is written to document\n");
    id3_test();
    printf("test is done\n");
    system("pause");   
}




 





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值