1. 关于ID3和C4.5的原理介绍这里不赘述,网上到处都是,可以下载讲义c9641_c001.pdf或者参考李航的《统计学习方法》.(本文在博客园http://www.cnblogs.com/90zeng/p/ID3_C4_5.html同步发表)
2. 数据与数据处理
- 本文采用下面的训练数据:
- 数据处理:本文只采用了"Outlook", "Humidity", "Windy"三个属性,然后根据Humidity的值是否大于75,将Humidity的值归为两类,Play Golf 的值就是类别标签,只有yes 和 no两类
- 训练集是字符和数字的混合,这会给编程带来麻烦,所以首先把训练集用数字表示出来:
1 const unsigned att_num = 3; 2 const unsigned rule_num = 14; 3 string decision_tree_name("Play Golf ?"); 4 string attribute_names[] = { "Outlook", "Humidity", "Windy"}; 5 string attribute_values[] = { "Sunny", "Overcast", "Rainy", "> 75", "<= 75", "True", "False", "Yes", "No"}; 6 //训练集最后一列为分类标签,所以总列数为属性数加1 7 unsigned train_data[rule_num][att_num + 1] = { 8 { 0, 3, 6, 8},{ 0, 3, 5, 8},{ 1, 3, 6, 7}, 9 { 2, 3, 6, 7},{ 2, 3, 6, 7},{ 2, 4, 5, 8}, 10 { 1, 4, 5, 7},{ 0, 3, 6, 8},{ 0, 4, 6, 7}, 11 { 2, 3, 6, 7},{ 0, 4, 5, 7},{ 1, 3, 5, 7}, 12 { 1, 4, 6, 7},{ 2, 3, 5, 8} 13 };
以train_data的第一行{0, 3, 6, 8}为例解释:前三列值对应的属性与attribute_names中的元素分别对应,最后一列是类别标签的值,0 表示 attribute_values的第1个元素,即”Sunny“,类似3便是attribute_values的第4个元素"> 75",6 是 "False",8 是"No",所以{0, 3, 6, 8} 代表的实例就是:
其他实例都是以这样的方式数字化,方便编程.
3. 编写必要函数
因为ID3和C4.5都需要计算属性的信息增益,C4.5还需要计算属性的信息增益比,所正确编写这两个函数很重要,对比着讲义c9641_c001.pdf或者其他参考资料,编写出这两个函数.(代码最后附上)
4. 确定数据结构
这是最重要的一环,明确目的:构造一个决策树!这就直接决定了编程的正确或者难易,网上有很多例子,但是我觉得不够简洁,这里我采用一种简单且容易理解的方式:
1 struct Tree{
2 unsigned root;//节点属性值
3 vector<unsigned> branches;//节点可能取值
4 vector<Tree> children; //孩子节点
5 };
每一个决策树都是由根节点开始,然后有很多分支,分支连接着孩子节点,而每一个孩子节点以及这个孩子节点对应的所有子孙又可以组成一棵树,这是一个不断递归的过程,所以采用了上面的数据结构.
5. 构造决策树
有了上面的基础,开始着手构造决策树,根据规则选出某一属性作为根节点,根据根节点的取值确定分支,然后构造孩子节点,根据上面的陈述可以知道,每一个孩子节点及其后面的子孙又是一棵树,所以这是一个递归操作,即采用前面同样的方式来构造这个子树,以此类推。
6. 打印决策树
因为树的结构是递归的,所以打印决策树同样是一个递归的过程。
7. 代码实现
1 /*************************************************
2 Copyright:1.0
3 Author:太白路上的小混混
4 Date:2014-11-25
5 Description:ID3/C4.5 algorithm
6 **************************************************/
7
8 #include <iostream>
9 #include <cmath>
10 #include <vector>
11 #include <string>
12 #include <algorithm>
13 using namespace std;
14
15
16 const unsigned att_num = 3