数据挖掘之决策树ID3算法
/*
决策树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<<'\t'; if(!p->arrived_value.empty()){ cout<<p->arrived_value<<endl; for(int i=0;i<depth+1;i++) cout<<'\t'; } 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; }