决策树算法看起来很好玩(我没开玩笑我真的这么觉得),就是简单的树上走然后得到不同的结果,一开始我很纳闷,书上那些东西都是啥,什么信息增益,ID3,C4.5,都是什么玩意(不好好听课的后果),后来仔仔细细的把书看了三遍,终于明白是干嘛的了。其实是根据给出的数据,判断不同属性对于决策的贡献,然后获得这棵树的分支。理解之后实现起来就容易一些了,实现的时候也借鉴了别人的代码,研究透了之后自己写的,同时也做了一些改动,数组没有用指针(我不喜欢指针),用的数组+下标寻址。然后,这代码你要是让我再写一遍我告诉你,
我肯定写不出来。
当时写了有六七个小时呢(还是弄懂了别人代码的前提下,光看书实在不懂到底想干嘛)。
#include <cstdio>
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 10005;
struct P {
string name;
string val;
int id;
vector<int> son;
P():id(-1){}
}Tree[maxn];
int n;///元组数
int m;///数据条数
int tot;///节点总数
int root;///根节点id
vector<vector<string> > dataset;///所有数据
vector<string > col_name;///列名
map<string, vector<string> > Map;///存储属性对应的值
int nump[10];///用于标号
///判断是否是同一类
bool All(vector<vector<string> > D, string kind){
for(int i = 0;i < D.size();i ++)
if(D[i][n - 1] != kind) return false;
return true;
}
///取多数类的属性
string Most(vector<vector<string> > D){
int num = 0;
for(int i = 0;i < D.size();i ++)
if(D[i][n - 1] == "yes") num ++;
return (num > D.size() - num)?"yes":"no";
}
///计算Info
double Cal_Info(vector <vector <string> > D, string attribute, string value,bool first){
int t1 = 0, t2 = 0;
bool flag = false;
for(int j = 0; j < n; j ++){
if(flag) break;
if(col_name[j] == attribute){
for(int i = 0; i < D.size(); i++)
if((!first&&D[i][j] == value) || first){
if(D[i][n - 1] == "yes") t1++;
else t2++;
}
flag = true;
}
}
///属于同一类
if(t1 == 0 || t2 == 0 ) return 0;
double sum = t1 + t2;
double ans = -t1/sum*log(t1/sum)/log(2.0) - t2/sum*log(t2/sum)/log(2.0);
return ans;
}
///计算信息增益
double Cal_Gain(vector <vector <string> > D, string attribute){
double Info = Cal_Info(D, attribute, "", true);
double InfoA = 0;
vector<string> v = Map[attribute];
vector<double> ans;
vector<int> vv;
int t1;
for(int i = 0; i < v.size(); i++){
t1 = 0;
for(int k = 0; k < n - 1; k++)
if(col_name[k] == attribute){
for(int j = 0; j < D.size(); j++)
if(D[j][k] == v[i])
t1 ++;
}
vv.push_back(t1);
}
for(int i = 0; i < v.size();i ++)
ans.push_back(1.0 * vv[i] / (D.size()));
double temp;
for(int i = 0; i < v.size();i ++){
temp = Cal_Info(D, attribute, v[i], false);
InfoA += ans[i] * temp;
}
return (Info - InfoA);
}
///建树过程
int Generate_decision_tree(int pos, vector<vector<string> > D, vector <string> attribute_list){
if(Tree[pos].id == -1) Tree[pos].id = pos;
///所有数据结果都是yes
if(All(D, "yes")){
Tree[pos].name = "yes";
return pos;
}///所有数据结果都是no
else if(All(D, "no")){
Tree[pos].name = "no";
return pos;
}
///属性判断完毕仍不能分开,取多数
if(attribute_list.size() == 0){
Tree[pos].name = Most(D);
return pos;
}
///计算最大增益
double maxg = -1;
int maxpos = 0;
for(int i = 0;i < attribute_list.size();i ++){
double curg = Cal_Gain(D, attribute_list[i]);
if(curg > maxg){
maxg = curg;
maxpos = i;
}
}
///根据最大增益对应的属性进行划分
Tree[pos].name = attribute_list[maxpos];
///获得属性对应的所有值
vector<string> v = Map[attribute_list[maxpos]];
int p;
for(p = 0;p < n;p ++)
if(col_name[p] == attribute_list[maxpos])
break;
vector<string> tempatt;
vector<vector<string > > tempD;
for(int i = 0;i < attribute_list.size();i ++)
if(i != maxpos) tempatt.push_back(attribute_list[i]);
for(int i = 0;i < v.size();i ++){
for(int j = 0;j < D.size();j ++)
if(D[j][p] == v[i])
tempD.push_back(D[j]);
int curnum = ++ tot ;
Tree[curnum].val = v[i];
if(tempD.size() == 0) Tree[curnum].name = Most(D);
else Generate_decision_tree(curnum, tempD, tempatt);
Tree[pos].son.push_back(curnum);
tempD.clear();
}
return pos;
}
///遍历
void show(int id, int dep){
for(int i = 0;i < dep;i ++) cout<<" ";
if(Tree[id].val != ""){
dep ++;
cout<<++ nump[dep]<<"."<<Tree[id].val<<endl;
for(int i = 0;i < dep;i++)
cout<<" ";
}
cout<<Tree[id].name<<endl;
for(int i = 0;i < Tree[id].son.size();i++){
show(Tree[id].son[i], dep+1);
}
}
int main(){
freopen("1.txt", "r", stdin);
memset(nump, 0, sizeof nump);
string t;
vector<string > data;
cin >> n >> m;
for(int i = 0;i < n;i ++)
cin >> t, col_name.push_back(t);
for(int i = 0;i < m;i ++){
data.clear();
for(int j = 0;j < n;j ++){
cin >> t;
data.push_back(t);
}
dataset.push_back(data);
}
///获得每个属性对应的值
vector<string> vt;
set<string> ss;
for(int i = 0;i < n - 1;i ++){
vt.clear();
ss.clear();
for(int j = 0;j < m;j ++)
if(!ss.count(dataset[j][i])){
ss.insert(dataset[j][i]);
vt.push_back(dataset[j][i]);
}
Map[col_name[i]] = vt;
}
vt = col_name;
vt.erase(vt.end() - 1);
root = Generate_decision_tree(0, dataset, vt);
show(root,0);
return 0;
}
然后输入数据的话我就不附了,就是列数 记录条数,酱,主题的递归算法,过程跟书上是一样的。哦对了,我的书是机械工业出版社的数据挖掘,黑色大理石纹,我觉得还不错。起码我从书上还真学到了点啥(虽然后期忙着复习没有好好听课)。