决策树模型

决策树python实现和sklearn'实现

 

目录

python实现和sklearn'实现

1.概览

2.实现

2.1python实现

2.1.1构建决策树过程图例

2.2 sklearn实现

3.CART决策树


 

1.概览

2.实现

2.1python实现

# -*- coding: utf-8 -*-
"""
Created on Sun Aug 26 11:17:27 2018

决策树-python实现
    数据源:给定外貌特征实现男女分类
    
    注意:
      1.数学:
        1.log函数   log以2为底,a为对数
          import math
          math.log(a,2)
        2.去重
          set(a)   获取a去重后所有的类别
        3.去索引
          value是series格式
          continues_value=[i for i in value]
        4,求字典的key最大值
            final=max(t_ent,key=t_ent.get)
        5.map(function(),iterator)
           它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。
            map(lambda x: x ** 2, [1, 2, 3, 4, 5])  # 使用 lambda 匿名函数
               [1, 4, 9, 16, 25]
          
      2.代码
        1.信息熵计算
          遍历每行数据,找到目标数据,计数存储到字典
          循环字典,求信息熵
        2.先对连续性字段进行二分法寻找最优切分点,找到后,对数据进行离散化(将数据以切分点进行分类,大于是一类,小于是一类)
        3.难点:二叉有向树建树!
               遍历二叉有向树

"""
"""生成示例数据
"""
import numpy as np
import pandas as pd

def create_data():
    data_value = np.array([['long', 'thick', 175, 'no', 'man'], ['short', 'medium', 168, 'no', 'man'],
                     ['short', 'thin', 178, 'yes', 'man'], ['short', 'thick', 172, 'no', 'man'],
                     ['long', 'medium', 163, 'no', 'man'], ['short', 'thick', 180, 'no', 'man'],
                     ['long', 'thick', 173, 'yes', 'man'], ['short', 'thin', 174, 'no', 'man'],
                     ['long', 'thin', 164, 'yes', 'woman'], ['long', 'medium', 158, 'yes', 'woman'],
                     ['long', 'thick', 161, 'yes', 'woman'], ['short', 'thin', 166, 'yes', 'woman'],
                     ['long', 'thin', 158, 'no', 'woman'], ['short', 'medium', 163, 'no', 'woman'],
                     ['long', 'thick', 161, 'yes', 'woman'], ['long', 'thin', 164, 'no', 'woman'],
                     ['short', 'medium', 172, 'yes','woman']] )
    columns = np.array(['hair', 'voice', 'height', 'ear_stud','labels'])
    #print('data_value',data_value)
    data=pd.DataFrame(data_value,columns=columns)
    return data
data = create_data()
print(data)

"""计算信息熵:即根节点纯度(根节点:男性,女性)
"""
import math
def get_Ent(data):
    num_sample=len(data)
    label_counts={}
    for i in range(num_sample):
        each_data=data.iloc[i,:]
        current_label=each_data['labels']
        
        if current_label not in label_counts.keys():
            label_counts[current_label]=0
        label_counts[current_label]+=1
    Ent=0
    for key in label_counts:
        prob=label_counts[key]/num_sample
        Ent-=prob*math.log(prob,2)
    return Ent
base_ent=get_Ent(data)
"""计算 以hair分类信息增益
"""
def get_gain(data,base_ent,feature):
    feature_list=data[feature]
    unique_value=set(feature_list)
    feature_ent=0
    for each_feature in unique_value:
        temp_data=data[data[feature]==each_feature]
        weight=len(temp_data)/len(feature_list)
        temp_ent=weight*get_Ent(temp_data)
        feature_ent+=temp_ent
    gain=base_ent-feature_ent
    return gain


"""连续值的划分点求信息增益
"""
def get_splitpoint(data,base_ent,feature):
    value=data[feature].sort_values().astype(np.float64)
    #print('value',value)
    continues_value=[i for i in value]
    print('continues_value',continues_value)
    t_set=[]
    t_ent={}
    for i in range(len(continues_value)-1):
        temp_t=(continues_value[i]+continues_value[i+1])/2
        t_set.append(temp_t)
    for each_t in t_set:
        temp1_data=data[data[feature].astype(np.float64)>each_t]
        temp2_data=data[data[feature].astype(np.float64)<each_t]
        weight1=len(temp1_data)/len(data)
        weight2=len(temp2_data)/len(data)
        temp_ent=base_ent-weight1*get_Ent(temp1_data)-weight2*get_Ent(temp2_data)
        t_ent[each_t]=temp_ent     #t_ent {158.0: 0.1179805181500242
    final_t=max(t_ent,key=t_ent.get)
    return final_t
final_t=get_splitpoint(data,base_ent,'height')
#print('final',final)  
"""数据预处理,将数据划分为倆类
在找到最佳划分点之后,将  <t<t  的值设为 0,将  >t>t  的值设为 1。
此处是将>t的标志位>t 相反:<t
"""   
def choice_1(x, t):
    if x > t:
        return ">{}".format(t)
    else:
        return "<{}".format(t)
deal_data = data.copy()
# 使用lambda和map函数将 height 按照final_t划分为两个类别
deal_data["height"] = pd.Series(
    map(lambda x: choice_1(int(x), final_t), deal_data["height"]))
print('deal_data',deal_data)
"""选择最优划分特征
遍历所有特征
"""
def choose_feature(data):
    """
    参数:
    data -- 数据集
    返回:
    best_feature -- 最优的划分特征
    """
    num_features = len(data.columns) - 1  # 特征数量
    base_ent = get_Ent(data)
    best_gain = 0.0  # 初始化信息增益
    best_feature = data.columns[0]
    for i in range(num_features):  # 遍历所有特征
        temp_gain = get_gain(data, base_ent, data.columns[i])    # 计算信息增益
        if (temp_gain > best_gain):  # 选择最大的信息增益
            best_gain = temp_gain
            best_feature = data.columns[i]
    return best_feature  # 返回最优特征
print(choose_feature(deal_data))
"""构建决策树
"""


def create_tree(data):
    """
    参数:
    data -- 数据集

    返回:
    tree -- 以字典的形式返回决策树
    """
    feature_list = data.columns[:-1].tolist()
    label_list = data.iloc[:, -1]
    if len(data["labels"].value_counts()) == 1:
        leaf_node = data["labels"].mode().values
        print('data["labels"].mode()',data["labels"].mode(),'leaf_node',leaf_node)
        return leaf_node            # 第一个递归结束条件:所有的类标签完全相同
    if len(feature_list) == 1:
        leaf_node = data["labels"].mode().values
        return leaf_node   # 第二个递归结束条件:用完了所有特征
    print('len(data["labels"].value_counts())',len(data["labels"].value_counts()))
    print('len(feature_list)',len(feature_list),'feature_list',feature_list)
    best_feature = choose_feature(data)   # 最优划分特征#height,ear_stud,voice,,
    print('best_feature',best_feature)#
    tree = {best_feature: {}}#1.{'height': {}} 2.{'ear_stud': {}}
    print('tree',tree)
    feat_values = data[best_feature]#series 1--<172.0   2-->172.0
    print('feat_values',feat_values)
    unique_value = set(feat_values)#1.{'<172.0', '>172.0'}  2.{'no', 'yes'}
    print('unique_value',unique_value)
    for value in unique_value:
        temp_data = data[data[best_feature] == value]#1.过滤掉>172数据,剩下height<172.0的数据 2.过滤yes数据,剩下no的数据。。。。3遍历到voice时,只剩一个数据short  thick    man
        print('temp_data-1',temp_data)
        temp_data = temp_data.drop([best_feature], axis=1)#删除身高<172.0这一列,2.删除no这一列
        print('temp_data-2',temp_data)
        tree[best_feature][value] = create_tree(temp_data)#在这里递归,3voice/thick时候,tree-1 ['man'],然后遍历voice/medium,tree-1 ['man'],voice/thin['woman']
        print('tree-1',tree[best_feature][value])#{'voice': {'thick': array(['man'], dtype=object), 'medium': array(['man'], dtype=object), 'thin': array(['woman'], dtype=object)}}
    return tree
tree = create_tree(deal_data)
print(tree)


"""决策分类
"""
def classify(tree, test):
    """
    参数:
    data -- 数据集
    test -- 需要测试的数据

    返回:
    class_label -- 分类结果
    """
    first_feature = list(tree.keys())[0]  # 获取根节点   ['height']
    print('tree-keys',list(tree.keys()))
    feature_dict = tree[first_feature]  # 根节点下的树   {'<172.0': {'ear_stud': {'no':
    print('feature_dict',feature_dict)
    print('feature_dict.keys()',feature_dict.keys())   #  dict_keys(['<172.0', '>172.0'])
    labels = test.columns.tolist()
    value = test[first_feature][0]   # <172.0
    print('value',value)
    for key in feature_dict.keys():
        if value == key:
            if type(feature_dict[key]).__name__ == 'dict':  # 判断该节点是否为叶节点
                class_label = classify(feature_dict[key], test)  # 采用递归直到遍历到叶节点
            else:
                class_label = feature_dict[key]
    return class_label
test = pd.DataFrame({"hair": ["long"], "voice": ["thin"], "height": [163], "ear_stud": ["yes"]})
test["height"] = pd.Series(map(lambda x: choice_1(int(x), final_t), test["height"]))
print('111',test)
a=classify(tree,test)
print('222',a)


















        

部分结果图

2.1.1构建决策树过程图例

2.2 sklearn实现

# -*- coding: utf-8 -*-
"""
Created on Mon Sep  3 21:48:42 2018
决策树分类--sklearn实现
 数据:
   学生成绩数据集 course-13-student.csv,一共有 395 条数据,26 个特征
   http://labfile.oss.aliyuncs.com/courses/1081/course-13-student.csv
   特征太多,随机筛选这些特征作为分类特征
         school:学生所读学校(GP,MS)   sex: 性别(F:女,M:男)
        address: 家庭住址(U:城市,R:郊区)
        Pstatus: 父母状态(A:同居,T:分居)
        Pedu: 父母学历由低到高
        reason: 选择这所学校的原因(home:家庭,course:课程设计,reputation:学校地位,other:其他)
        guardian: 监护人(mother:母亲,father:父亲,other:其他)
        studytime: 周末学习时长
        schoolsup: 额外教育支持(yes:有,no:没有)
        famsup: 家庭教育支持(yes:有,no:没有)
        paid: 是否上补习班(yes:是,no:否)
        higher: 是否想受更好的教育(yes:是,no:否)
        internet: 是否家里联网(yes:是,no:否)
        G1: 一阶段测试成绩
        G2: 二阶段测试成绩
        G3: 最终成绩
"""

"""导入数据集并预览
"""
import pandas as pd

stu_grade = pd.read_csv('course-13-student.csv')
print(stu_grade.head())
new_data = stu_grade.iloc[:, [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 15, 24, 25, 26]]
"""数据预处理 : 处理连续字段

成绩划分 : 成绩 G1,G2,G3 根据分数进行等级划分,将 0-4 划分为 bad,5-9 划分为 medium ,10-15 good,16--优秀
"""
def choice_2(x):
    x = int(x)
    if x < 5:
        return "bad"
    elif x >= 5 and x < 10:
        return "medium"
    elif x >= 10 and x < 15:
        return "good"
    else:
        return "excellent"


stu_data = new_data.copy()
stu_data["G1"] = pd.Series(map(lambda x: choice_2(x), stu_data["G1"]))
stu_data["G2"] = pd.Series(map(lambda x: choice_2(x), stu_data["G2"]))
stu_data["G3"] = pd.Series(map(lambda x: choice_2(x), stu_data["G3"]))


"""对 Pedu (父母教育程度)也进行划分
"""
def choice_3(x):
    x = int(x)
    if x > 3:
        return "high"
    elif x > 1.5:
        return "medium"
    else:
        return "low"


stu_data["Pedu"] = pd.Series(map(lambda x: choice_3(x), stu_data["Pedu"]))
print(stu_data.head())
"""特征值替换
字符特征转为数字0-1特征
"""

def replace_feature(data):
    """
    参数:
    data -- 数据集

    返回:
    data -- 将特征值替换后的数据集
    """
    for each in data.columns:  # 遍历每一个特征名称
        feature_list = data[each]
        unique_value = set(feature_list)
        i = 0
        for fea_value in unique_value:
            data[each] = data[each].replace(fea_value, i)
            i += 1
    return data
stu_data = replace_feature(stu_data)
print('stu_data.head(10)',stu_data.head(10))
"""数据集划分: 7:3

x_train对应y_train, x_test,对应y_test
"""
from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(stu_data.iloc[:, :-1], stu_data["G3"], 
                                                    test_size=0.3, random_state=5)

"""决策树构建

DecisionTreeClassifier(criterion=’gini’,random_state=None)
criterion :表示特征划分方法选择,默认为 gini (在后面会讲到),可选择为 entropy (信息增益)。
ramdom_state :表示随机数种子,当特征特别多时 sicit-learn 为了提高效率,随机选取部分特征来进行特征选择,
即找到所有特征中较优的特征。
"""
from sklearn.tree import DecisionTreeClassifier

dt_model = DecisionTreeClassifier(criterion='entropy', random_state=34)
"""模型预测
"""
dt_model.fit(x_train,y_train) # 使用训练集训练模型
y_predict = dt_model.predict(x_test) # 使用模型对测试集进行预测
y_predict
"""分类准确率计算
一致的/总共的
"""


def get_accuracy(test_labels, pred_lables):
    """
    参数:
    test_labels -- 测试集的真实值
    pred_labels -- 测试集的预测值

    返回:
    accur -- 准确率
    """
    correct = np.sum(test_labels == pred_lables)  # 计算预测正确的数据个数
    n = len(test_labels)  # 总测试集数据个数
    accur = correct/n
    return accur


print('predict',get_accuracy(y_test, y_predict))


#"""展示决策树生成
#"""
#from sklearn.tree import export_graphviz
#import graphviz
#import numpy as np
#
#img = export_graphviz(
#    dt_model, out_file=None,
#    feature_names=stu_data.columns[:-1].values.tolist(),  # 传入特征名称
#    class_names=np.array(["bad", "medium", "good", "excellent"]),  # 传入类别值
#    filled=True, node_ids=True,
#    rounded=True)
#
#graphviz.Source(img)  # 展示决策树

部分结果图

3.CART决策树

分类与回归树(classification and regression tree, CART)同样也是应用广泛的决策树学习算法,CART 算法是按照特征划分,由树的生成和树的剪枝构成,既可以进行分类又可以用于回归,按照作用将其分为决策树和回归树,由于本实验设计为决策树的概念,所以回归树的部分有兴趣的同学可以自己查找相关资料进一步学习。

CART决策树的构建和常见的 ID3 和 C4.5 算法的流程相似,但在特征划分选择上CART选择了 基尼指数 作为划分标准。数据集 D 的纯度可用基尼值来度量:

基尼指数表示随机抽取两个样本,两个样本类别不一致的概率,基尼指数越小则数据集的纯度越高。同样对于每一个特征值的基尼指数计算,其和 ID3 、 C4.5 相似,定义为:

 

在进行特征划分的时候,选择特征中基尼值最小的作为最优特征划分点。

实际上,在应用过程中,更多的会使用 基尼指数 对特征划分点进行决策,最重要的原因是计算复杂度相较于 ID3 和 C4.5 小很多(没有对数运算)。

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值