程序目的:根据一定数量的训练样本,生成决策树。依靠生成的决策树测试预测样本的分类。
代码:
简单版:
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");
}