实验三:CART回归决策树python实现(两个测试集)(二)|机器学习


python实现

分步

  • 划分数据子集(左子树划分比指定值小的样本集合,右子树划分比指定值大的样本集合)

    import numpy as np
    #获取数据子集,分类与回归的做法相同
    #将数据集根据划分特征切分为两类
    def split_dataset(data_x,data_y,fea_axis,fea_value):
        '''
        input:data_x(ndarry):特征值
              data_y(ndarry):标签值
              fea_axis(int):进行划分的特征编号(列数)
              fea_value(int):进行划分的特征对应特征值
        output:data_x[equal_Idx],data_y[equal_Idx](ndarry):特征值等于(大于等于)目标特征值的样本与标签
               data_x[nequal_Idx],data_y[nequal_Idx](ndarry):特征值不等于(小于)目标特征的样本与标签
        '''
        if  isinstance(fea_value,int) or isinstance(fea_value,float): 
            #如果特征值为浮点数(连续特征值),那么进行连续值离散化
            equal_Idx = np.where(data_x[:,fea_axis]<=fea_value) #找出特征值大于等于fea_alue的样本序号
            nequal_Idx = np.where(data_x[:,fea_axis]>fea_value) #找出特征值小于fea_alue的样本序号
        else:
            equal_Idx = np.where(data_x[:,fea_axis]==fea_value) #找出特征值等于fea_alue的样本序号
            nequal_Idx = np.where(data_x[:,fea_axis]!=fea_value) #找出特征值不等于fea_alue的样本序号
        return data_x[equal_Idx],data_y[equal_Idx],data_x[nequal_Idx],data_y[nequal_Idx]
    
  • 叶子结点的均值计算

    import numpy as np
    #叶子结点均值计算
    def reg_leaf(data_y):
        '''
        input:data_y(array):标签值
        output:(float)均值
        '''
        return np.mean(data_y)
    
  • 计算数据集总方差

    #计算数据集的总方差
    def reg_err(data_y):
        '''
        input:data_y(array):标签值
        output:(float):总方差
        '''
        return np.var(data_y)*len(data_y)
    
  • 选取划分特征以及对应的特征值

    def classify_get_best_fea(data_x,data_y,ops=(1,4)):
        '''
        input:data_x(ndarry):特征值
              data_y(array):标签值
              ops(tuple):第一个数为决策树停止划分的最小精度,第二个数为决策树停止划分的最小划分数
        output:best_fea_idx(int):最好划分特征的下标
               best_fea_val(float):最好划分特征对应的特征值
        '''
        m,n = np.shape(data_x)
        final_s = ops[0] #停止的精度
        final_n = ops[1] #停止的样本最小划分数
        #只有一类样本时,输出叶子结点,以及它对应的均值
        if len(np.unique(data_y))==1:
            return None,reg_leaf(data_y)
        
        #获取最优特征和特征值
        total_err = reg_err(data_y)  #总的误差
        best_err = np.inf
        best_fea_idx = 0
        best_fea_val = 0
        
        for i in range(n):
            feas = np.unique(data_x[:,i])
            for fea_val in feas:
                data_D1_x,data_D1_y,data_D2_x,data_D2_y = split_dataset(data_x,data_y,i,fea_val)
                #不满足最小划分集合,不进行计算
                if data_D1_x.shape[0]<final_n or data_D2_x.shape[0]<final_n:
                    continue
                con_err = reg_err(data_D1_y)+reg_err(data_D2_y)
                if con_err<best_err:
                    best_err = con_err
                    best_fea_idx = i
                    best_fea_val = fea_val
                    
        #预剪枝,求解的误差小于最小误差停止继续划分
        if total_err-best_err<final_s:
            return None,reg_leaf(data_y)
        
        #一直无法进行划分,在这里进行处理
        data_D1_x,data_D1_y,data_D2_x,data_D2_y = split_dataset(data_x,data_y,best_fea_idx,best_fea_val)
        if data_D1_x.shape[0]<final_n or data_D2_x.shape[0]<final_n:
            return None,reg_leaf(data_y)
        
        return best_fea_idx,best_fea_val
    
  • CART回归树的生成(递归生成)

    def reg_create_tree(data_x,data_y,ops=(1,4)):
        fea_idx,fea_val = classify_get_best_fea(data_x,data_y,ops)
        if fea_idx == None:
            return fea_val
        
        #递归建立CART回归决策树
        my_tree = {}
        my_tree['fea_idx'] = fea_idx
        my_tree['fea_val'] = fea_val
        data_D1_x,data_D1_y,data_D2_x,data_D2_y = split_dataset(data_x,data_y,fea_idx,fea_val)
        
        my_tree['left'] = reg_create_tree(data_D1_x,data_D1_y,ops)
        my_tree['right'] = reg_create_tree(data_D2_x,data_D2_y,ops)
    
        return my_tree
    

    字典格式为:{’‘fea_idx’’:’‘当前最好划分特征下标’’,’‘fea_val’’:’‘对应特征值’’,’‘left’’:{…},“right”:{…}}。

  • 预测函数

    #测试操作
    import re
    #预测一条测试数据结果
    def classify(inputTree,testdata):
        '''
        input:inputTree(dict):CART分类决策树
              xlabel(list):特征属性列表
              testdata(darry):一条测试数据特征值
        output:classLabel(int):测试数据预测结果
        '''
        first_fea_idx = inputTree[list(inputTree.keys())[0]]  #对应的特征下标
        fea_val = inputTree[list(inputTree.keys())[1]]  #对应特征的分割值
    
        classLabel = 0.0 #定义变量classLabel,默认值为0
        
        if testdata[first_fea_idx]>=fea_val:  #进入右子树
            if type(inputTree['right']).__name__ == 'dict':
                classLabel = classify(inputTree['right'],testdata)
            else:
                classLabel = inputTree['right']
        else:  #进入左子树
            if type(inputTree['left']).__name__ == 'dict':
                classLabel = classify(inputTree['left'],testdata)
            else:
                classLabel = inputTree['left']
    
        return round(classLabel,2)
    
    #预测所有测试数据结果
    def classifytest(inputTree, testDataSet):
        '''
        input:inputTree(dict):训练好的决策树
              xlabel(list):特征值标签列表
              testDataSet(ndarray):测试数据集
        output:classLabelAll(list):测试集预测结果列表
        '''    
        classLabelAll = []#创建空列表
        for testVec in testDataSet:#遍历每条数据
            classLabelAll.append(classify(inputTree, testVec))#将每条数据得到的特征标签添加到列表
        return  np.array(classLabelAll)
    

源代码(全部)

import numpy as np
import re
#获取数据子集,分类与回归的做法相同
#将数据集根据划分特征切分为两类
def split_dataset(data_x,data_y,fea_axis,fea_value):
    '''
    input:data_x(ndarry):特征值
          data_y(ndarry):标签值
          fea_axis(int):进行划分的特征编号(列数)
          fea_value(int):进行划分的特征对应特征值
    output:data_x[equal_Idx],data_y[equal_Idx](ndarry):特征值等于(大于等于)目标特征值的样本与标签
           data_x[nequal_Idx],data_y[nequal_Idx](ndarry):特征值不等于(小于)目标特征的样本与标签
    '''
    if  isinstance(fea_value,int) or isinstance(fea_value,float): 
        #如果特征值为浮点数(连续特征值),那么进行连续值离散化
        equal_Idx = np.where(data_x[:,fea_axis]<=fea_value) #找出特征值大于等于fea_alue的样本序号
        nequal_Idx = np.where(data_x[:,fea_axis]>fea_value) #找出特征值小于fea_alue的样本序号
    else:
        equal_Idx = np.where(data_x[:,fea_axis]==fea_value) #找出特征值等于fea_alue的样本序号
        nequal_Idx = np.where(data_x[:,fea_axis]!=fea_value) #找出特征值不等于fea_alue的样本序号
    return data_x[equal_Idx],data_y[equal_Idx],data_x[nequal_Idx],data_y[nequal_Idx]

#叶子结点均值计算
def reg_leaf(data_y):
    '''
    input:data_y(array):标签值
    output:(float)均值
    '''
    return np.mean(data_y)

#计算数据集的总方差
def reg_err(data_y):
    '''
    input:data_y(array):标签值
    output:(float):总方差
    '''
    return np.var(data_y)*len(data_y)

def classify_get_best_fea(data_x,data_y,ops=(1,4)):
    '''
    input:data_x(ndarry):特征值
          data_y(array):标签值
          ops(tuple):第一个数为决策树停止划分的最小精度,第二个数为决策树停止划分的最小划分数
    output:best_fea_idx(int):最好划分特征的下标
           best_fea_val(float):最好划分特征对应的特征值
    '''
    m,n = np.shape(data_x)
    final_s = ops[0] #停止的精度
    final_n = ops[1] #停止的样本最小划分数
    #只有一类样本时,输出叶子结点,以及它对应的均值
    if len(np.unique(data_y))==1:
        return None,reg_leaf(data_y)
    
    #获取最优特征和特征值
    total_err = reg_err(data_y)  #总的误差
    best_err = np.inf
    best_fea_idx = 0
    best_fea_val = 0
    
    for i in range(n):
        feas = np.unique(data_x[:,i])
        for fea_val in feas:
            data_D1_x,data_D1_y,data_D2_x,data_D2_y = split_dataset(data_x,data_y,i,fea_val)
            #不满足最小划分集合,不进行计算
            if data_D1_x.shape[0]<final_n or data_D2_x.shape[0]<final_n:
                continue
            con_err = reg_err(data_D1_y)+reg_err(data_D2_y)
            if con_err<best_err:
                best_err = con_err
                best_fea_idx = i
                best_fea_val = fea_val
                
    #预剪枝,求解的误差小于最小误差停止继续划分
    if total_err-best_err<final_s:
        return None,reg_leaf(data_y)
    
    #一直无法进行划分,在这里进行处理
    data_D1_x,data_D1_y,data_D2_x,data_D2_y = split_dataset(data_x,data_y,best_fea_idx,best_fea_val)
    if data_D1_x.shape[0]<final_n or data_D2_x.shape[0]<final_n:
        return None,reg_leaf(data_y)
    
    return best_fea_idx,best_fea_val

def reg_create_tree(data_x,data_y,ops=(1,4)):
    '''
    input:data_x(ndarry):特征值
          data_y(array):标签值
          ops(tuple):第一个数为决策树停止划分的最小精度,第二个数为决策树停止划分的最小划分数
    output:my_tree(dict):生成的CART决策树字典
    '''
    fea_idx,fea_val = classify_get_best_fea(data_x,data_y,ops)
    if fea_idx == None:
        return fea_val
    
    #递归建立CART回归决策树
    my_tree = {}
    my_tree['fea_idx'] = fea_idx
    my_tree['fea_val'] = fea_val
    data_D1_x,data_D1_y,data_D2_x,data_D2_y = split_dataset(data_x,data_y,fea_idx,fea_val)
    
    my_tree['left'] = reg_create_tree(data_D1_x,data_D1_y,ops)
    my_tree['right'] = reg_create_tree(data_D2_x,data_D2_y,ops)

    return my_tree

#预测一条测试数据结果
def classify(inputTree,testdata):
    '''
    input:inputTree(dict):CART分类决策树
          xlabel(list):特征属性列表
          testdata(darry):一条测试数据特征值
    output:classLabel(int):测试数据预测结果
    '''
    first_fea_idx = inputTree[list(inputTree.keys())[0]]  #对应的特征下标
    fea_val = inputTree[list(inputTree.keys())[1]]  #对应特征的分割值

    classLabel = 0.0 #定义变量classLabel,默认值为0
    
    if testdata[first_fea_idx]>=fea_val:  #进入右子树
        if type(inputTree['right']).__name__ == 'dict':
            classLabel = classify(inputTree['right'],testdata)
        else:
            classLabel = inputTree['right']
    else:
        if type(inputTree['left']).__name__ == 'dict':
            classLabel = classify(inputTree['left'],testdata)
        else:
            classLabel = inputTree['left']

    return round(classLabel,2)

#预测所有测试数据结果
def classifytest(inputTree, testDataSet):
    '''
    input:inputTree(dict):训练好的决策树
          xlabel(list):特征值标签列表
          testDataSet(ndarray):测试数据集
    output:classLabelAll(list):测试集预测结果列表
    '''    
    classLabelAll = []#创建空列表
    for testVec in testDataSet:#遍历每条数据
        classLabelAll.append(classify(inputTree, testVec))#将每条数据得到的特征标签添加到列表
    return  np.array(classLabelAll)

测试集1(波士顿房价数据集)

一共拥有十三个属性如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oppXYnrA-1637414783461)(C:\Users\CQU  CJ\AppData\Roaming\Typora\typora-user-images\image-20211120195430055.png)]

导入数据集并训练CART回归树:

#波士顿房价数据集
from sklearn.datasets import load_boston
boston = load_boston()

data = boston.data
target = boston.target

# X = data[:200,:]
# y = target[:200]

x_train,x_test,y_train,y_test = train_test_split(X,y,test_size = 0.3,random_state = 666)
#生成CART回归树
cartTree = reg_create_tree(x_train,y_train)
print(cartTree)

训练得到的CART树如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUabw0SQ-1637414783466)(C:\Users\CQU  CJ\AppData\Roaming\Typora\typora-user-images\image-20211120195617697.png)]

进行预测可以得到结果如下图所示:

classlist=classifytest(cartTree,x_test)
print('预测数据',classlist)
print('真实数据'.y_test)
print("平均误差为:",abs(np.sum(classlist)-np.sum(y_test))/len(y_test))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RWAMfHOU-1637414783468)(C:\Users\CQU  CJ\AppData\Roaming\Typora\typora-user-images\image-20211120195809831.png)]


测试集2(糖尿病数据集)

一共拥有十个属性如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9XVINE4F-1637414783471)(C:\Users\CQU  CJ\AppData\Roaming\Typora\typora-user-images\image-20211120201500374.png)]

导入数据集并训练CART回归树:

#糖尿病数据集
from sklearn.datasets import load_diabetes
diabetes = load_diabetes()

data = diabetes.data
target = diabetes.target

X = data[:500,:]
y = target[:500]

x_train,x_test,y_train,y_test = train_test_split(X,y,test_size = 0.2,random_state = 666)
#生成CART回归树
cartTree = reg_create_tree(x_train,y_train)
print(cartTree)

训练得到的CART树如下图所示(500条数据):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e38oaK5p-1637414783474)(C:\Users\CQU  CJ\AppData\Roaming\Typora\typora-user-images\image-20211120201224522.png)]

进行预测可以得到结果如下图所示:

classlist=classifytest(cartTree,x_test)
print('预测数据',classlist)
print('真实数据',y_test)
print("平均误差为:",abs(np.sum(classlist)-np.sum(y_test))/len(y_test))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-46tBXvGm-1637414783475)(C:\Users\CQU  CJ\AppData\Roaming\Typora\typora-user-images\image-20211120201337750.png)]


总结

(1)使用CART回归树进行预测的数据集标签值都是连续的,当然也可以使用离散的标签值做分类问题,只不过这样做没有必要。对于各项特征对应的特征值可以是连续的也可以是离散的,不同特征之间的数量级也可以不同,因为划分时各特征之间是相互独立的。(对于神经网络来说则需要进行归一化处理)

(2)可以使用平均误差(误差之和的平均值)、均方误差(误差的平方和的平均值)对模型的精度进行评估。

(3)计算方法与CART分类树的基尼系数不同,CART回归树使用均方差的方法。

  • 4
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
好的,下面是关于决策树CART算法Python实现示例。 首先,我们需要导入相关的库: ```python import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import accuracy_score ``` 接下来,我们需要加载数据集: ```python iris = load_iris() X = pd.DataFrame(iris.data, columns=iris.feature_names) y = pd.DataFrame(iris.target, columns=['target']) ``` 然后,我们将数据集分成训练集和测试集: ```python X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) ``` 接下来,我们定义一个函数来计算基尼不纯度: ```python def gini_impurity(y): _, counts = np.unique(y, return_counts=True) probabilities = counts / len(y) return 1 - np.sum(probabilities ** 2) ``` 然后,我们定义一个函数来计算基尼不纯度的加权平均值: ```python def weighted_gini_impurity(groups): total_size = sum(len(group) for group in groups) gini = 0 for group in groups: size = len(group) if size == 0: continue score = gini_impurity(group['target']) gini += score * (size / total_size) return gini ``` 接下来,我们定义一个函数来拆分数据集: ```python def test_split(index, value, X, y): left_mask = X.iloc[:, index] < value right_mask = X.iloc[:, index] >= value left = {'X': X[left_mask], 'y': y[left_mask]} right = {'X': X[right_mask], 'y': y[right_mask]} return left, right ``` 然后,我们定义一个函数来选择最佳的数据集拆分: ```python def get_best_split(X, y): best_index, best_value, best_score, best_groups = None, None, float('inf'), None for index in range(X.shape[1]): for value in X.iloc[:, index]: groups = test_split(index, value, X, y) score = weighted_gini_impurity(list(groups.values())) if score < best_score: best_index, best_value, best_score, best_groups = index, value, score, groups return {'feature_index': best_index, 'feature_value': best_value, 'groups': best_groups} ``` 接下来,我们定义一个函数来创建一个叶节点: ```python def create_leaf_node(y): return y['target'].mode()[0] ``` 然后,我们定义一个函数来创建一个决策树: ```python def create_decision_tree(X, y, max_depth, min_size, depth): best_split = get_best_split(X, y) left, right = best_split['groups'].values() del(best_split['groups']) if not left or not right: return create_leaf_node(pd.concat([left, right], axis=0)) if depth >= max_depth: return create_leaf_node(y) if len(left) < min_size: left = create_leaf_node(left) else: left = create_decision_tree(left['X'], left['y'], max_depth, min_size, depth+1) if len(right) < min_size: right = create_leaf_node(right) else: right = create_decision_tree(right['X'], right['y'], max_depth, min_size, depth+1) return {'left': left, 'right': right, **best_split} ``` 最后,我们定义一个函数来进行预测: ```python def predict(node, row): if row[node['feature_index']] < node['feature_value']: if isinstance(node['left'], dict): return predict(node['left'], row) else: return node['left'] else: if isinstance(node['right'], dict): return predict(node['right'], row) else: return node['right'] ``` 现在我们已经定义了所有必要的函数,我们可以用以下代码来创建并测试我们的决策树模型: ```python tree = create_decision_tree(X_train, y_train, max_depth=5, min_size=10, depth=1) y_pred = np.array([predict(tree, row) for _, row in X_test.iterrows()]) print('Accuracy:', accuracy_score(y_test, y_pred)) ``` 这就是一个基于CART算法决策树Python实现示例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比奇堡咻飞兜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值