西瓜书中的决策树算法实现(ID3)

一些关于决策树自己的理解

决策树是根据信息增益自己选择规则,并且递归构建树的过程。
所谓的信息增益可以理解为:原本系统是混乱的,当选择一个属性划分,划分后两个子系统的混乱程度是否减轻了;比如;原来系统正负样本,比例为1:1 ,如果你选择一个属性进行划分后,两个子系统的正负样本比例变成了 8:2 和3:7 ,那么这次 的划分就是有意义的,而根据信息增益,更大的信息增益,表示更好的划分属性,从而大大减小系统的混乱程度也就是熵。
【注】熵本来是物理学的概念,便是一个区域内,事物的混乱程度,越混乱熵越大;也就是所,世界上所有物质都是在熵增的过程 中,比如桌子会越来越来乱,地上会越来越脏,但是也可以通过消耗能量的方式,降低一个区域的熵,比如消耗生物能收拾下桌子,通过消耗热能把碎铁融成铁块。通过选择属性降低数据集的混乱程度。
本次使用的数据集是【西瓜数据集2.0】

编号,色泽,根蒂,敲声,纹理,脐部,触感,好瓜
1,青绿,蜷缩,浊响,清晰,凹陷,硬滑,是
2,乌黑,蜷缩,沉闷,清晰,凹陷,硬滑,是
3,乌黑,蜷缩,浊响,清晰,凹陷,硬滑,是
4,青绿,蜷缩,沉闷,清晰,凹陷,硬滑,是
5,浅白,蜷缩,浊响,清晰,凹陷,硬滑,是
6,青绿,稍蜷,浊响,清晰,稍凹,软粘,是
7,乌黑,稍蜷,浊响,稍糊,稍凹,软粘,是
8,乌黑,稍蜷,浊响,清晰,稍凹,硬滑,是
9,乌黑,稍蜷,沉闷,稍糊,稍凹,硬滑,否
10,青绿,硬挺,清脆,清晰,平坦,软粘,否
11,浅白,硬挺,清脆,模糊,平坦,硬滑,否
12,浅白,蜷缩,浊响,模糊,平坦,软粘,否
13,青绿,稍蜷,浊响,稍糊,凹陷,硬滑,否
14,浅白,稍蜷,沉闷,稍糊,凹陷,硬滑,否
15,乌黑,稍蜷,浊响,清晰,稍凹,软粘,否
16,浅白,蜷缩,浊响,模糊,平坦,硬滑,否
17,青绿,蜷缩,沉闷,稍糊,稍凹,硬滑,否

源码分析下:
首先导入读取csv,并把提取列名,供以后使用:

df=pd.read_csv("./ml2.0.csv")
# 属性集合
attr=df.columns.values.tolist()[1:]
data_org=np.array(df[attr[0:]])
static_attr=df.columns.values.tolist()[1:]#这里的属性 不改变,仅仅作为索引

获得各个列名下的详细分类:

attr_dict={}#用于记录每一个属性的取值
for  x in attr[:-1]:
    temp=np.array(df[x])
    attr_dict[x]=set(temp)

attr_dict 格式如下:

{'色泽': {'乌黑', '浅白', '青绿'}, 
 '根蒂': {'蜷缩', '硬挺', '稍蜷'}, 
 '敲声': {'浊响', '清脆', '沉闷'},
 '纹理': {'清晰', '稍糊', '模糊'}, 
 '脐部': {'平坦', '稍凹', '凹陷'}, 
 '触感': {'硬滑', '软粘'}
}

判断一个数据集中,是否全部为正样本或者全部为负样本

def lable_is_same(D):
    l=[D[i][-1] for i in range(len(D))] 
    #将数据集最后一列,也就是label 标签提取出来.
    #然后使用set 去掉重复元素,
    #如果只有一个样本,表明全正或者全负
    return len(list(set(l)))==1

下面判断,是否数据集中属性全部都相同:

def all_attr_is_same(D,d_attr):
    if D.shape[0]==0:return True #如果是空集
    for x  in d_attr:
        index=static_attr.index(x)
        for i in D[0:] :
            if i[index]!=D[0][index]:
                return False
    return True

以上是两个结束条件的判断:
在第一种情况下;所有的值都是正或者负样本:
这时候,只要把这个分类的label值作为叶子节点,结束构造即可。
比如 在纹理分类下:纹理模糊的瓜都不是好瓜,因此,可得出结论
在这里插入图片描述在第二种情况下:数据集合中所有属性都一样,一次不用再进行划分了
比如,属性集合中只有[颜色,根蒂]两个属性,但是他们都相同,无法进行划分
在这里插入图片描述
这时候,将只要把出现最多的label值作为叶子节点,父节点是上一个属性,这里选择是好瓜。然后结束构造即可。

下面进行选择最佳节点的操作:
首先,我们需要一个函数统计一下,一个样本集中,正样本和负样本的比例,并根据这个概率计算信息熵

def collect(D):
    if D.shape[0]==0:
        return 0.0000000001
    count= 0
    for i in D:
       count=count+1 if  i[-1]=='是' else count
    return count /(D.shape[0])

下面进行信息熵的计算

def Ent(D):
    p=collect(D)
    if (fabs(1.0 - p) < 0.000001) or (fabs(0.0 - p) < 0.000001):
        return 0 #表示完全分割开了
    return  -p*np.log2(p)-(1-p)*np.log2(1-p)

计算信息增益:信息增益是什么,原来系统是混乱的,当以一个属性区分后,混乱度减小了,那怎么量化这个减小呢? 信息增益就是就是标准。

def Gain(D,a):
    G=Ent(D)
    index = static_attr.index(a)
    lis=attr_dict[a]
    temp_dict=split_data(D,a)
    sum=0
    for x in lis:
        d=np.array(temp_dict[x])
        sum+=(d.shape[0]/D.shape[0])*Ent(d)
    return G-sum
#由于合理的划分时熵减小的过程,所以G在前

信息增益的函数中,我们使用了一个划分函数 ,这个划分函数的作用时,将数据集根据属性进行划分。比如 以纹理划分:则将数据集划分为三类,一类纹理清晰,一类纹理稍糊和纹理模糊。下面是具体实现:

def split_data(D,a):
    index = static_attr.index(a)
    lis=attr_dict[a]
    temp_dict={}
    for i in lis:
        temp_dict[i]=[]
    for i in D:
        for at in lis:
            if i[index]==at:
                temp_dict[at].append(i)
                break;
    return temp_dict

下面进行最优属性的选择:什么是最优属性,那还用说,肯定是信息增益最大的属性呗,我们只要找到信息增益最大的那个属性就可以了。

def select_opt_attr(D,d_attr):
    li=[]
    for x in d_attr:
        li.append(Gain(D,x))
    return d_attr[li.index(max(li))]

做完了所有的准备工作,下面我们开始主算法

def TreeGenerate(D,d_attr,node,father):#ID3算法
    if  D.shape[0]==0:
        return
    if label_is_same(D):
        node['final']=D[-1][-1]
        return
    if len(d_attr)==0 or all_attr_is_same(D,d_attr):# 属性集为空 或者所有样本在说有属性上取值相同
        node['final']='是' if collect(D)>0.5 else '否'
        return
    #选择最优的划分属性:
    opt_attr=select_opt_attr(D,d_attr)
    d_attr.remove(opt_attr)
    #根据最优属性划分集合:
    attr_dict=split_data(D,opt_attr)
    node[opt_attr]={}
    for i in attr_dict:
        d1=np.array(attr_dict[i])
        node[opt_attr][i]={}
        TreeGenerate(d1,d_attr[:],node[opt_attr][i],i)# 递归调用生成函数

运行结果如下:

{'纹理': {
     '清晰': {
         '根蒂': {
              '硬挺': {'final': '否'}, 
              '稍蜷': {
                   '色泽': {
                        '浅白': {}, 
                        '乌黑': {
                             '触感': {
                                  '软粘': {'final': '否'}, 
                                  '硬滑': {'final': '是'}
                                  }
                                  }, 
                         '青绿': {'final': '是'}
                         }
                         }, 
               '蜷缩': {'final': '是'}
               }
               }, 
      '稍糊': {
           '触感': {
                  '软粘': {'final': '是'}, 
                  '硬滑': {'final': '否'}
                  }
                  }, 
      '模糊': {'final': '否'}}}

在这里插入图片描述到此整个算法就实现了。这次的设计是基于 ID3 算法 依然存在很多问题,如过拟合等。

  • 0
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值