/*
决策树ID3算法思想:从根节点开始,每一步的选择决定朝下一层的哪个节点走去,到达叶子节点即是它所属的类别,举个例子:假如要判断某一天是否适合打网球,分成两类:是和否。
有3个因素对其有影响,分别是天气,温度和是否有风,天气有3个值:晴,多云,有雨。温度有3个值:高,正常,低。是否有风有两个值:是和否。现在我要选择3个因素中哪一个作为
根节点最好呢?这里涉及一个计算信息增益的问题,可以通过数学的相关知识得到(不具体说),如果信息增益越大,越适合作为根节点,假如现在天气的信息增益最大,那么天气因素作为
根节点。并产生3个不同的分支,即天气的3个值的选择,再去递归构建子树。直到所有的属性选完或者子样本已经全部归于一类。建立好这棵树之后,现在来一组新样本,不知道它最终的
分类,可以通过决策树知道他的类别。
*/
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAXLEN 6 /**输入每行的数据个数**/
/**
多叉树的实现
广义表
父指针表示法,适于经常找父节点的应用
子女链表示法,适于经常找子节点的应用
左长子,右兄弟表示法,实现起来比较麻烦
每个节点的所有孩子用vector保存
教训:数据结构的设计很重要,本算法采用5比较合适,同时
注意维护剩余样例和剩余信息,建树时横向遍历靠循环属性的值
纵向遍历靠递归调用
**/
vector<vector<string> > state; /**实例集**/
vector<string> item(MAXLEN); /**对应一行实例集**/
vector<string> attribute_row; /**保存属性行数据**/
string end("end"); /**输入结束**/
string yes("yes");
string no("no");
string blank("");
map<string,vector<string> > map_attribute_values; /**存储属性对应的所有值**/
int tree_size=0;
struct Node{
string attribute; /**属性值**/
string arrived_value; /**到达的属性值**/
vector<Node *> childs; /**所有的孩子**/
Node(){
attribute=blank;
arrived_value=blank;
}
};
Node *root;
/**根据数据实例计算属性与值组成的map**/
void ComputeMapFrom2DVector(){
unsigned int i,j,k;
bool exited=false;
vector<string> values;
for(i=1;i<MAXLEN-1;i++){ /**第一个和最后一个不属于属性**/
for(j=1;j<State.size();j++){
for(k=0;k<values.size();k++){
if(!values[k].compare(state[j][i])) exited=true; /**存在**/
}
if(!exited){
values.push_back(state[j][i]);
/**注意vector都是从前面插入的,注意更新it,始终指向vector头**/
}
exited=false;
}
map_attribute_values[state[0][i]]=values; /**这一属性包含的所有属性值**/
values.erase(values.begin(),values.end());
}
}
/**根据具体属性和值计算熵**/
double ComputeEntropy(vector<vector<string> >& remain_state,string& attribute,string& value,bool ifparent){
vector<int> count(2,0);
unsigned int i,j;
bool done_flag=false; /**哨兵值**/
for(j=1;j<MAXLEN;j++){
if(done_flag) break;
if(!attribute_row[j].compare(attribute)){ /**计算该属性**/
for(i=1;i<remain_state.size();i++){
if((!ifparent&&!remain_state[i][j].compare(value)) || ifparent){
/**ifparent记录是否算父节点**/
if(!remain_state[i][MAXLEN-1].compare(yes)){
count[0]++;
}
else count[1]++;
}
}
done_flag=true; /**没什么用,只是检测是否有这个属性**/
}
}
if(count[0]==0 || count[1]==0) return 0;/**全部是正实例或者负实例,值为0**/
double sum=count[0]+count[1];
double entropy=-count[0]/sum*log(count[0]/sum)/log(2.0)-count[1]/sum*log(count[1]/sum)/log(2.0); /**换底公式**/
return entropy;
}
/**计算按照属性attribute划分当前剩余实例的信息增益**/
double ComputeGain(vector<vector<string> >& remain_state,string attribute){
unsigned int j,k,m;
double parent_entropy=ComputeEntropy(remain_state,attribute,blank,true);
double children_entropy=0;
/**然后求做划分后各个值的熵**/
vector<string> values=map_attribute_values[attribute];
vector<double> ratio;
vector<int> count_values;
int tempint;
for(m=0;m<values.size();m++){
tempint=0;
for(k=1;k<MAXLEN-1;k++){
if(!attribute_row[k].compare(attribute)){
for(j=1;j<remain_state.size();j++){
if(!remain_state[j][k].compare(values[m])){
tempint++; /**计算每一个分支属性值的个数**/
}
}
}
}
count_values.push_back(tempint);
}
for(j=0;j<values.size();j++){
ratio.push_back((double)count_values[j]/(double)(remain_state.size()-1)); /**个数/总数**/
}
double temp_entropy;
for(j=0;j<values.size();j++){
temp_entropy=ComputeEntropy(remain_state,attribute,values[j],false);
children_entropy+=ratio[j]*temp_entropy;
}
return (parent_entropy-children_entropy);
}
int FindAttriNumByName(string attri){
for(i=0;i<MAXLEN;i++){
if(!state[0][i].compare(attri)) return i;
}
cerr<<"can't find the numth of attribute"<<endl;
return 0;
}
/**找出样例中占多数的正/负性**/
string MostCommonLabel(vector<vector<string> >& remain_state){
int p=0,n=0;
for(unsigned int i=0;i<remain_state.size();i++){ /**计算yes或no的数量**/
if(!remain_state[i][MAXLEN-1].compare(yes)) p++;
else n++;
}
if(p>=n) return yes;
else return no;
}
/**判断样例是否正负性都为label**/
bool AllTheSameLabel(vector<vector<string> >& remain_state,string label){
int count=0;
for(unsigned int i=0;i<remain_state.size();i++){
if(!remain_state[i][MAXLEN-1].compare(label)) count++; /**最后一个属性是yes或no**/
}
if(count==remain_state.size()-1) return true; /**第一行不是**/
return false;
}
/**
计算信息增益,DFS构建决策树
current_node为当前的节点
remain_state为剩余部分类的样例
remain_attribute 为剩余还没考虑的属性
返回根节点指针
**/
Node* BuildDecisionTreeDFS(Node *p,vector<vector<string>> remain_state,vector<string> remain_attribute){
if(p==NULL) p=new Node(); /**为空创建新节点**/
if(AllTheSameLabel(remain_state,yes)){ /**全部是yes,作为树叶节点返回**/
p->attribute=yes;
return p;
}
if(AllTheSameLabel(remain_state,no)){ /**全部是no,作为树叶节点返回**/
p->attribute=no;
return p;
}
if(remain_attribute.size()==0){ /**所有属性已分完**/
string label=MostCommonLabel(remain_state); /**取最多的**/
p->attribute=label;
return p;
}
double max_gain=0,temp_gain;
vector<string>::iterator max_it=remain_attribute.begin();
vector<string>::iterator it1;
for(it1=remain_attribute.begin();it1!=remain_attribute.end();it1++){
temp_gain=ComputeGain(remain_state,(*it1)); /**计算此属性的gain值**/
if(temp_gain>max_gain){ /**取最大的**/
max_gain=temp_gain;
max_it=it1;
}
}
/**下面根据max_it指向的属性来划分当前样例,更新样例集和属性集**/
vector<string> new_attribute;
vector<vector<string>> new_state;
for(vector<string>::iterator it2=remain_attribute.begin();it2!=remain_attribute.end();it2++){
if((*it2).compare(*max_it)) new_attribute.push_back(*it2); /**不等于该属性**/
}
/**确定了最佳划分属性,注意保存**/
p->attribute=*max_it;
vector<string> values=map_attribute_values[*max_it];
int attribute_num=FindAttriNumByName(*max_it); /**找到属性下标**/
new_state.push_back(attribute_row);
for(vector<string>::iterator it3=values.begin();it3!=values.end();it3++){
for(unsigned int i=1;i<remain_state.size();i++){
if(!remain_state[i][attribute_num].compare(*it3)){
new_state.push_back(remain_state[i]);
}
}
Node *new_node=new Node();
new_node->arrived_value=*it3; /**分支属性**/
if(new_state.size()==1){ /**当前没有这个分支的样例,叶子节点**/
new_node->attribute=MostCommonLabel(remain_state);
}
else BuildDecisionTreeDFS(new_node,new_state,new_attribute);
p->childs.push_back(new_node); /**增加节点**/
new_state.erase(new_state.begin()+1,new_state.end());
}
return p;
}
void Input(){
string s;
while(cin>>s,s.compare(end)!=0){ /**直到遇到end结束**/
item[0]=s;
for(int i=1;i<MAXLEN;i++){
cin>>item[i];
}
state.push_back(item);
}
for(int j=0;j<MAXLEN;j++){
attribute_row.push_back(state[0][j]); /**属性行信息**/
}
}
void PrintTree(Node *p,int depth){
for(int i=0;i<depth;i++) cout<<' ';
if(!p->arrived_value.empty()){
cout<<p->arrived_value<<endl;
for(int i=0;i<depth+1;i++) cout<<' ';
}
cout<<p->attribute<<endl;
for(vector<Node*>::iterator it=p->childs.begin();it!=p->childs.end();it++){
PrintTree(*it,depth+1);
}
}
void FreeTree(Node *p){
if(p==NULL) return;
for(vector<Node*>::iterator it=p->childs.begin();it!=p->childs.end();it++) FreeTree(*it);
delete p;
tree_size++;
}
int main(){
Input();
vector<string> remain_attribute;
string outlook("Outlook");
string Temprature("Temperature");
string Humidity("Humidity");
string Wind("Wind");
remain_attribute.push_back(outlook);
remain_attribute.push_back(Temprature);
remain_attribute.push_back(Humidity);
remain_attribute.push_back(Wind);
vector<vector<string>> remain_state;
for(unsigned int i=0;i<state.size();i++){
remain_state.push_back(state[i]);
}
ComputeMapFrom2DVector();
root=NULL;
root=BuildDecisionTreeDFS(root,remain_state,remain_attribute); /**递归搜**/
cout<<"the decision tree is:"<<endl;
PrintTree(root,0);
FreeTree(root);
cout<<endl;
cout<<"tree_size"<<tree_size<<endl;
return 0;
}