数据挖掘 - 算法 - ID3 - 转自 http://www.cnblogs.com/dztgc/archive/2013/04/22/3036529.html

1  简介

  决策树学习是一种逼近离散值目标函数的方法,在这种学习到的函数被表示为一棵决策树。

2 决策树表

  决策树通过把实例从根节点排列到某个叶子结点来分类实例,叶子结点即为实例所属的分类。树上的每一个结点指定了对实例的某个属性的测试,并且该结点的每一个后续分支对应于该属性的一个可能值。

分类实例的方法是从这棵树的根节点开始,测试这个结点指定的属性,然后按照给定实例的该属性值对应的树枝向下移动。然后这个过程在以新结点为根的子树上重复。

画出了一棵典型的学习到的决策树。

                                        图1

这棵决策树根据天气情况分类“星期六上午是否适合打网球”。例如,下面的实例:

<   Outlook=Sunny,Temperature= Hot,Humidity = High ,Wind= Strong  >

将被沿着这棵决策树的最左分支向下排列,因而被评定为反例(也就是这棵树预测这个实例Play Tennis = No)。

这棵树以及表 3-2 中用来演示 ID3 学习算法的例子摘自(Quinlan1986)

通常决策树代表实例属性值约束的合取(conjunction )的析取式(disjunction)。 从树根到树叶的每一条路径对应一组属性测试的合取,树本身对应这些合取的析取。

例如,图1 表示的决策树对应于以下表达式:

(Outlook=Sunny ? Humidity= Normal) ?(Outlook=Overcast) ?(Outlook=Rain ? Wind=Weak)

3 决策树的ID3算法

基本的ID3 算法通过自顶向下构造决策树来进行学习。

构造过程是从“哪一个属性将在树的根结点被测试?”这个问题开始的。

为了回答这个问题,使用统计测试来确定每一个实例属性单独分类训练样例的能力。

(1)分类能力最好的属性被选作树的根结点的测试。

(2)然后为根结点属性的每个可能值产生一个分支,并把训练样例排列到适当的分支(也就是,样例的该属性值对应的分支)之下。

(3)然后重复整个过程,用每个分支结点关联的训练样例来选取在该点被测试的最佳属性。

这形成了对合格决策树的贪婪搜索(greedy search ),也就是算法从不回溯重新考虑以前的选择。

伪代码

3.1 哪个属性是最佳的分类属性

ID3 算法的核心问题是选取在树的每个结点要测试的属性。

我们希望选择的是最有助于分类实例的属性。那么衡量属性价值的一个好的定量标准是什么呢?

这里将定义一个统计属性,称为“信息增益(information gain )”,用来衡量给定的属性区分训练样例的能力。

ID3 算法在增长树的每一步使用这个信息增益标准从候选属性中选择属性。

3.1.1用熵度量样例的均一性

为了精确地定义信息增益,我们先定义信息论中广泛使用的一个度量标准,称为熵(entropy),它刻画了任意样例集的纯度( purity)。

给定包含关于某个目标概念的正反样例的样例集S ,那么S 相对这个布尔型分类的熵为:

Entropy(S ) =-Plog2P-PΘlog2PΘ 

其中P 是在S 中正例的比例,PΘ是在S 中负例的比例。在有关熵的所有计算中我们定义0 log0 为0 。

举例说明,假设S 是一个关于某布尔概念的有 14 个样例的集合,它包括 9 个正例和5 个反例(我们采用记号[9+ ,5 -]来概括这样的数据样例)。

那么S 相对于这个布尔分类的熵(Entropy)为: 

Entropy([9+,5-]) =-(9/14)log2(9/14) - (5/14)log2(5/14)

至此我们讨论了目标分类是布尔型的情况下的熵。更一般的,如果目标属性具有 c个不同的值,那么S 相对于 c 个状态(c-wise )的分类的熵定义为:

Entropy(S )=Σ -Pi*log2Pi(1 ≤ i≤ c)

3.1.2用信息增益度量期望的熵降低

已经有了熵作为衡量训练样例集合纯度的标准,现在可以定义属性分类训练数据的效力的度量标准。这个标准被称为“信息增益(information gain )”。

简单的说,一个属性的信息增益就是由于使用这个属性分割样例而导致的期望熵降低。更精确地讲,一个属性A 相对样例集合 S 的信息增益Gain(S, A)被定义为:

其中 Values (A)是属性A所有可能值的集合,SV 是S 中属性A的值为v 的子集(也就是,Sv ={s∈ S |A(s )=v } )。

请注意,等式的第一项就是原来集合S 的熵,第二项是用A分类S 后熵的期望值。

这个第二项描述的期望熵就是每个子集的熵的加权和,权值|Sv|/|S|为属于S的样例占原始样例S 的比例 。

所以Gain(S , A)是由于知道属性A的值而导致的期望熵减少。换句话来讲,Gain (S , A)是由于给定属性A的值而得到的关于目标函数值的信息。

例如,假定 S 是一套有关天气的训练样例,描述它的属性包括可能是具有 Weak和Strong 两个值的 Wind。像前面一样,假定 S 包含14 个样例,[9+ ,5 -]。在这 14 个样例中,假定正例中的 6 个和反例中的2 个有Wind =Weak,其他的有Wind =Strong。由于按照属性Wind 分类14 个样例得到的信息增益可以计算如下



实验举例:

训练样本:

D1    Sunny        Hot    High        Weak    No
D2    Sunny        Hot    High        Strong    No
D3    Overcast    Hot    High        Weak    Yes
D4    Rain        Mild    High        Weak    Yes
D5    Rain        Cool    Normal        Weak    Yes
D6    Rain        Cool    Normal        Strong    No
D7    Overcast    Cool    Normal        Strong    Yes
D8    Sunny        Mild    High        Weak    No
D9    Sunny        Cool    Normal        Weak    Yes
D10    Rain        Mild    Normal        Weak    Yes
D11    Sunny        Mild    Normal        Strong    Yes
D12    Overcast    Mild    High        Strong    Yes
D13    Overcast    Hot    Normal        Weak    Yes
D14    Rain        Mild    High        Strong    No

测试样本:

D1    Sunny        Hot    High        Weak
D2    Sunny        Hot    High        Strong
D3    Overcast    Hot    High        Weak
D4    Rain        Mild    High        Weak
D5    Rain        Cool    Normal        Weak
D6    Rain        Cool    Normal        Strong
D7    Overcast    Cool    Normal        Strong
D8    Sunny        Mild    High        Weak
D9    Sunny        Cool    Normal        Weak
D10    Rain        Mild    Normal        Weak
D11    Sunny        Mild    Normal        Strong
D12    Overcast    Mild    High        Strong
D13    Overcast    Hot    Normal        Weak
D14    Rain        Mild    High        Strong

头文件

head.h

#ifndef ID3_H_INCLUDED
#define ID3_H_INCLUDED
#include <map>
#include <fstream>
#include <vector>
#include <set>
#include <iostream>
#include <cmath>
using namespace std;

const int DataRow=14;
const int DataColumn=6;

const int testRow=14;
const int testColumn=5;
struct Node{
    double value;      // 标签值,1为YES 0为No
    int attrid;       //属性标号
    int attrvalue;      //属性值
    vector<Node*> childNode;
};

#endif // ID3_H_INCLUDED

C++代码

#include "id3.h"

string DataTable[DataRow][DataColumn];          //保存训练样例
string TestTable[testRow][DataColumn];          //保存测试样例
map<string ,int> string2int;
set<int> S;
set<int> Attributes;
string attrName[DataColumn]=
{"Day","OutLook","Temperature","Humidity","Wind","PlayTennis"};
string attrValue[DataColumn][DataColumn]=
{
    {},
    {"Sunny","Overcast","Rain"},// sunny 1,overcast 2,rain3
    {"Hot","Mild","Cool"},//hot 1,mild 2, cool 3
    {"High","Normal"},//High 1,normal 2
    {"Weak","Strong"}, // weak 1,strong 2
    {"Yes","No"}  //yes 1,no 2

};
int attrCount[DataColumn]={14,3,3,2,2,2};
double lg2(double n)
{
    return log(n)/log(2);
}
void Init()                 //初始化
{
    ifstream fin("dataset.txt");
    for(int i=0;i<DataRow;++i)
    {
        for(int j=0;j<DataColumn;++j)
        {
            fin>>DataTable[i][j];
        }
    }
    fin.close();
    for(int i=1;i<=DataColumn-1;++i)
    {
        string2int[attrName[i]]=i;
        for(int j=0;j<attrCount[i];++j)
            string2int[attrValue[i][j]]=j;
    }
    for(int i=0;i<DataRow;i++)
        S.insert(i);
    for(int i=1;i<=DataColumn-2;i++)
        Attributes.insert(i);
}
double Entropy(const set<int> &s)               //计算熵值
{
    double yes=0,no=0;
    for(set<int>::size_type i=1;i<s.size();i++)
    {
        if(string2int[DataTable[i][DataColumn-1]]==0)
            yes++;
        else
            no++;
    }
    if(no==0||yes==0)
        return 0;
    double Py=yes/s.size();
    double Pn=no/s.size();
    double ans=-1*Py*lg2(Py)+-1*Pn*lg2(Pn);
    return ans;
}
double Gain(const set<int> &s,int attrid)           //计算信息增益值
{
    double ans=0;
    int attrcount = attrCount[attrid];
    double sumEntropy = Entropy(s);
    set<int> *pset = new set<int>[attrcount];
    for(set<int>::const_iterator iter=s.begin();iter!=s.end();iter++)
    {

        pset[string2int[DataTable[*iter][attrid]]].insert(*iter);
    }
    for(int i=0;i<attrcount;i++)
    {
        ans-=(double)pset[i].size()/(double)s.size()*Entropy(pset[i]);
    }
    return sumEntropy-ans;
}
int FinderBestAttribute(const set<int> &s,const set<int> &attr)      //找到最佳分类属性
{
    double maxg=0;
    int k=-1;
    for(set<int>::const_iterator iter=attr.begin();iter!=attr.end();++iter)
    {
        double tem=Gain(s,*iter);
        if(tem>maxg)
        {
            maxg=tem;
            k=*iter;
        }
    }
    int sum=s.size();
    sum=attr.size();
    if(k==-1)
        cout<<"FinderBestAttribute Error!"<<endl;
    return k;
}

Node * Id3_solution(set<int> s,set<int> attr)
{
    Node * now = new Node();
    now->value=-1;
    if(attr.empty())
        return NULL;
    int yes=0,no=0,sum=s.size();
    for(set<int>::iterator iter=s.begin();iter!=s.end();iter++)
    {
        if(DataTable[*iter][DataColumn-1]=="Yes")
            yes++;
        else
            no++;
    }
    if(yes==sum||no==sum)
    {
     now->value=yes/sum;
     return now;
    }

    int bestattrid = FinderBestAttribute(s,attr);       //找到最佳的分类属性
    now->attrid=bestattrid;
    attr.erase(attr.find(bestattrid));
    vector<set<int> > child=vector<set<int> >(attrCount[bestattrid]);
    for(set<int>::iterator iter=s.begin();iter!=s.end();iter++)             //插入孩子结点
    {
        int id = string2int[DataTable[*iter][bestattrid]];
        child[id].insert(*iter);
    }
    for(int i=0;i<child.size();i++)         //对孩子结点进行递归调用
    {
        Node *rel = Id3_solution(child[i],attr);
        rel->attrvalue=i;
        now->childNode.push_back(rel);
    }
    return now;
}
void test(Node * Root)          //用测试样例进行测试
{
    Node* pnow=Root;
    ifstream fin("test.txt");
    for(int i=0;i<testRow;i++)
    {
        for(int j=0;j<testColumn;j++)
            fin>>TestTable[i][j];
    }
    fin.close();
    for(int i=0;i<testRow;i++)
    {
        pnow=Root;
        while(true)
        {
            if(pnow->value==1)
                {TestTable[i][DataColumn-1]="yes";break;}
            else if(pnow->value==0)
                {TestTable[i][DataColumn-1]="no";break;}
            for(vector<Node*>::iterator iter=pnow->childNode.begin();iter!=pnow->childNode.end();++iter)
            {
                if((*iter)->attrvalue==string2int[TestTable[i][pnow->attrid]])
                {pnow=*iter;
                break;}
            }
        }
    }
}
int main()
{
    Init();
    Node * Root = Id3_solution(S,Attributes);
    test(Root);
    for(int i=0;i<testRow;i++)
    {
        for(int j=0;j<DataColumn;j++)
            cout<<TestTable[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}

结果:

时间: 2024-10-12 13:10:06

数据挖掘 - 算法 - ID3 - 转自 http://www.cnblogs.com/dztgc/archive/2013/04/22/3036529.html的相关文章

bat常用命令,转【http://www.cnblogs.com/yplong/archive/2013/04/02/2996550.html】

1.@它的作用是隐藏它后面这一行的命令本身(只能影响当前行).2.echo中文为"反馈"."回显"的意思.它其实是一个开关命令,就是说它只有两种状态:打开和关闭.于是就有了echo on和echo off两个命令了.直接执行echo命令将显示当前echo命令状态(off或on)执行echo off将关闭回显,它后面的所有命令都不显示命令本身,只显示执行后的结果.echo. :输出空行,即相当于输入一个回车:值得注意的是命令行中的"."要紧跟在EC

转载(http://www.cnblogs.com/flyfish2012/archive/2013/01/22/2871273.html)

做C#的同学们,都知道,一类只能有一个继承类,但可以实现多个接口.这句话就告诉我们:IEnumerable,ICollection,IList,List区别了 首先我看看 IEnumerable: // 摘要: // 公开枚举器,该枚举器支持在指定类型的集合上进行简单迭代. // // 类型参数: // T: // 要枚举的对象的类型. [TypeDependency("System.SZArrayHelper")] public interface IEnumerable<ou

tomcat通过conf-Catalina-localhost目录发布项目详解 摘自:http://www.cnblogs.com/iyangyuan/archive/2013/09/12/3316444.html

Tomcat发布项目的方式大致有三种,但小菜认为通过在tomcat的conf/Catalina/localhost目录下添加配置文件,来发布项目,是最佳选择. 因为这样对tomcat的入侵性最小,只需要新增一个配置文件,不需要修改原有配置:而且支持动态解析,修改完代码直接生效(修改配置除外). 但是网上关于这种方法的介绍很简单,小菜来补充一下. 1.直接在eclipse中添加一个server,添加过程中指明tomcat的路径即可. 2.在tomcat服务器的conf\Catalina\local

ghostDoct 使用 (转 http://www.cnblogs.com/RockyMyx/archive/2010/04/20/Project-Route-Using-GhostDoc.html)

一.简介 GhostDoc是Visual Studio的一个免费插件,可以为开发人员自动生成XML格式的注释文档. 二.下载 需要的朋友可以去这里下载,填个Email地址就可以下了:GhostDoc下载地址 三.安装 下载安装完成后,可以在Visual Studio的工具菜单下找到GhostDoc的身影. 在第一次使用时,会要求设置快捷键,默认的是Ctrl+Shift+S,如果这和你设置的快捷键有所冲突的话,可以在选择的下拉列表里另外选择一个. GhostDoc使用的优点自然是可以快速生成注释,

http://www.cnblogs.com/quanyongan/archive/2013/05/28/3103243.html

http://www.cnblogs.com/xdp-gacl/p/4242221.html http://blog.csdn.net/subuser/article/details/18988813/ http://jingyan.baidu.com/article/b87fe19e93b8905218356880.html

MAC和PHY的区别 (转自http://www.cnblogs.com/feitian629/archive/2013/01/25/2876857.html)

一块以太网网卡包括OSI(开方系统互联)模型的两个层.物理层和数据链路层.物理层定义了数据传送与接收所需要的电与光信号.线路状态.时钟基准.数据编码和电路等,并向数据链路层设备提供标准接口.数据链路层则提供寻址机构.数据帧的构建.数据差错检查.传送控制.向网络层提供标准的数据接口等功能. 1.网卡的基本结构 以太网卡中数据链路层的芯片一般简称之为MAC控制器,物理层的芯片我们简称之为PHY.许多网卡的芯片把MAC和PHY的功能做到了一颗芯片中,比如Intel 82559网卡的和3COM 3C90

Oracle常用的一些 数据字典 转https://www.cnblogs.com/neozhu/archive/2008/07/22/1248422.html

Oracle常用数据字典表 查看当前用户的缺省表空间 SQL>select username,default_tablespace from user_users; 查看当前用户的角色 SQL>select * from user_role_privs; 查看当前用户的系统权限和表级权限 SQL>select * from user_sys_privs; SQL>select * from user_tab_privs; 查看用户下所有的表 SQL>select * from

启动其他APK的Activity方法 (转至http://www.cnblogs.com/lijunamneg/archive/2013/02/26/2934060.html)

有两个app,分别叫做App1和App2.App1包含两个Activity,分别叫做App1_A和App1_B.其中App1_A是入口Activity.也就是App1_A设置intent-filter,action为<action android:name="android.intent.action.MAIN" />App2只有一个Activity,叫做App2_A.现在在App2_A中通过startActivity启动App1_A是没问题的.但是启动App1_B的时候报

z-index总结【转载http://www.cnblogs.com/mind/archive/2012/04/01/2198995.html】

元素位置重叠的背景常识 (x)html文档中的元素默认处于普通流(normal flow)中,也就是说其顺序由元素在文档中的先后位置决定,此时一般不会产生重叠(但指定负边距可能产生重叠).当我们用css为某个元素指定float浮动或者position定位后,元素的定位将会依情况发生如下改变: 1. 指定float值left/right 行内元素也会隐形变成块元素,元素会脱离文档的普通流,向左或右浮动,直到其外边缘碰到包含框或另一个浮动框. 2. 指定position值relative 可以相对于