利用决策树算法(非调库,python代码实现)在鲍鱼数据集上对年龄进行预测

在上一篇文章中,通过调取sklearn库中的tree模块来构建在鲍鱼数据集上的决策树,并对测试集鲍鱼的年龄进行预测,但是,通过调库的方式只能处理数值型的属性,若数据集中既包含连续型属性和离散型属性,则处理起来比较困难。而在本文中,将具体实现决策树的构建过程,并能分别处理连续型属性和离散型属性,最后对 鲍鱼数据集 中的鲍鱼年龄进行预测。

目录

前言

一、 数据集

二、步骤

 1.引入库

 2.读入数据

 3.数据预处理

 4.计算信息增益

 5.划分数据集

 6.计算最优划分属性

 7.构建决策树

三.预测

总结

参考文章:



前言

 本文将具体实现决策树的构建过程,并对 鲍鱼数据集 中的鲍鱼年龄进行预测。


以下是本篇文章正文内容

一、 数据集

训练集:

3e9b4fb2ac5d4627b464c43799d9dc76.png

测试集:

96e503abd4f84981befcbf022ac1bac8.png

数据集下载:

训练集:https://pan.baidu.com/s/1f4yf9vlndVar2J4cLqfjDg         提取码:U1S1

测试集:https://pan.baidu.com/s/1QQxqDsyoSrs529H49LLg2g         提取码:U2S2

二、步骤

 1.引入库

代码如下:(当然有些没有用到)

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import precision_score
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D
import copy

 2.读入数据

代码如下:

# 读取数据
train_datas=pd.read_csv(path1,header=None)
test_datas=pd.read_csv(path2,header=None)

path为文件路径

 3.数据预处理

#转换成列表
train_data=np.array(train_datas[1:][:]).tolist()
label=train_datas[:1][:]
labels=np.array(label).tolist()[0][:-1]
labelProperties = [0, 1, 1, 1, 1, 1, 1, 1]  # 属性的类型,0表示离散,1表示连续

test_data=test_datas.drop(columns=[8])
#转换成列表
testData=np.array(test_data[1:][:]).tolist()

 4.计算信息增益

def cal_Ent(dataSet):
    # 计算Ent(D)
    
    # 训练集大小
    length=len(dataSet)
    
    # 标签字典
    Ent_D_dict={}
    for data in dataSet:
        label=data[-1]
        if label in Ent_D_dict:
            Ent_D_dict[label]+=1
        else:
            Ent_D_dict[label]=1
    Ent_D=0
    for i in Ent_D_dict.values():
        odd=i/length
        Ent_D-=odd*np.log2(odd)
    return Ent_D

 5.划分数据集

(连续型属性):

# 划分数据集, axis:按第几个特征划分, value:划分特征的值, LorR: value值左侧(小于)或右侧(大于)的数据集
def splitDataSet_c(dataSet, axis, value, LorR='L'):
    retDataSet = []
    featVec = []
    if LorR == 'L':
        for featVec in dataSet:
            if float(featVec[axis]) < value:
                retDataSet.append(featVec)
    else:
        for featVec in dataSet:
            if float(featVec[axis]) > value:
                retDataSet.append(featVec)
    return retDataSet

(离散型属性):

def splitDataSet(dataSet, axis, value):
    retDataSet = []                                     #创建返回的数据集列表
    for featVec in dataSet:                             #遍历数据集
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]             #去掉axis特征
            reducedFeatVec.extend(featVec[axis+1:])     #将符合条件的添加到返回的数据集
            retDataSet.append(reducedFeatVec)
    return retDataSet 

 6.计算最优划分属性

# 选择最好的数据集划分方式
def chooseBestFeatureToSplit_c(dataSet, labelProperty):
    numFeatures = len(labelProperty)  # 特征数
    baseEntropy = cal_Ent(dataSet)  # 计算根节点的信息熵
    bestInfoGain = 0.0
    bestFeature = -1
    bestPartValue = None  # 连续的特征值,最佳划分值
    for i in range(numFeatures):  # 对每个特征循环
        featList = [example[i] for example in dataSet]
        uniqueVals = set(featList)  # 该特征包含的所有值
        newEntropy = 0.0
        bestPartValuei = None
        if labelProperty[i] == 0:  # 对离散的特征
            for value in uniqueVals:  # 对每个特征值,划分数据集, 计算各子集的信息熵
                subDataSet = splitDataSet(dataSet, i, value)
                prob = len(subDataSet) / float(len(dataSet))
                newEntropy += prob * cal_Ent(subDataSet)
        else:  # 对连续的特征
            sortedUniqueVals = list(uniqueVals)  # 对特征值排序
            sortedUniqueVals.sort()
            listPartition = []
            minEntropy = float("inf")
            for j in range(len(sortedUniqueVals) - 1):  # 计算划分点
                partValue = (float(sortedUniqueVals[j]) + float(
                    sortedUniqueVals[j + 1])) / 2
                # 对每个划分点,计算信息熵
                dataSetLeft = splitDataSet_c(dataSet, i, partValue, 'L')
                dataSetRight = splitDataSet_c(dataSet, i, partValue, 'R')
                probLeft = len(dataSetLeft) / float(len(dataSet))
                probRight = len(dataSetRight) / float(len(dataSet))
                Entropy = probLeft * cal_Ent(
                    dataSetLeft) + probRight * cal_Ent(dataSetRight)
                if Entropy < minEntropy:  # 取最小的信息熵
                    minEntropy = Entropy
                    bestPartValuei = partValue
            newEntropy = minEntropy
        infoGain = baseEntropy - newEntropy  # 计算信息增益
        print("第%d个特征的增益为%.3f" % (i, infoGain))             #打印每个特征的信息增益
        if infoGain > bestInfoGain:  # 取最大的信息增益对应的特征
            bestInfoGain = infoGain
            bestFeature = i
            bestPartValue = bestPartValuei
    return bestFeature, bestPartValue
def majorityCnt(classList):
    classCount = {}
    for vote in classList:                                        #统计classList中每个元素出现的次数
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)        #根据字典的值降序排序
    return sortedClassCount[0][0]

 7.构建决策树

# 创建树, 样本集 特征 特征属性(0 离散, 1 连续)
def createTree_c(dataSet, labels, labelProperty):
    print("---------------------------------------------")
    # print dataSet, labels, labelProperty
    classList = [example[-1] for example in dataSet]  # 类别向量
    if classList.count(classList[0]) == len(classList):  # 如果只有一个类别,返回
        return classList[0]
    if len(dataSet[0]) == 1:  # 如果所有特征都被遍历完了,返回出现次数最多的类别
        return majorityCnt(classList)
    bestFeat, bestPartValue = chooseBestFeatureToSplit_c(dataSet,
                                                        labelProperty)  # 最优分类特征的索引
    if bestFeat == -1:  # 如果无法选出最优分类特征,返回出现次数最多的类别
        return majorityCnt(classList)
    if labelProperty[bestFeat] == 0:  # 对离散的特征
        bestFeatLabel = labels[bestFeat]
        myTree = {bestFeatLabel: {}}
        labelsNew = copy.copy(labels)
        labelPropertyNew = copy.copy(labelProperty)
        del (labelsNew[bestFeat])  # 已经选择的特征不再参与分类
        del (labelPropertyNew[bestFeat])
        featValues = [example[bestFeat] for example in dataSet]
        uniqueValue = set(featValues)  # 该特征包含的所有值
        for value in uniqueValue:  # 对每个特征值,递归构建树
            subLabels = labelsNew[:]
            subLabelProperty = labelPropertyNew[:]
            myTree[bestFeatLabel][value] = createTree_c(
                splitDataSet(dataSet, bestFeat, value), subLabels,
                subLabelProperty)
    else:  # 对连续的特征,不删除该特征,分别构建左子树和右子树
        bestFeatLabel = labels[bestFeat] + '<' + str(bestPartValue)
        myTree = {bestFeatLabel: {}}
        subLabels = labels[:]
        subLabelProperty = labelProperty[:]
        # 构建左子树
        valueLeft = '是'
        myTree[bestFeatLabel][valueLeft] = createTree_c(
            splitDataSet_c(dataSet, bestFeat, bestPartValue, 'L'), subLabels,
            subLabelProperty)
        # 构建右子树
        valueRight = '否'
        myTree[bestFeatLabel][valueRight] = createTree_c(
            splitDataSet_c(dataSet, bestFeat, bestPartValue, 'R'), subLabels,
            subLabelProperty)
    return myTree

Trees = createTree_c(train_data, labels, labelProperties)

 运行结果:

951ff3fc7b144678ae63a486aa650a78.png

三.预测

# 测试算法
def classify_c(inputTree, featLabels, featLabelProperties, testVec):
    firstStr = list(inputTree.keys())[0]  # 根节点
    firstLabel = firstStr
    lessIndex = str(firstStr).find('<')
    if lessIndex > -1:  # 如果是连续型的特征
        firstLabel = str(firstStr)[:lessIndex]
    secondDict = inputTree[firstStr]
    featIndex = featLabels.index(firstLabel)  # 跟节点对应的特征
    classLabel = None
    for key in secondDict.keys():  # 对每个分支循环
        if featLabelProperties[featIndex] == 0:  # 离散的特征
            if testVec[featIndex] == key:  # 测试样本进入某个分支
                if type(secondDict[key]).__name__ == 'dict':  # 该分支不是叶子节点,递归
                    classLabel = classify_c(secondDict[key], featLabels,
                                           featLabelProperties, testVec)
                else:  # 如果是叶子, 返回结果
                    classLabel = secondDict[key]
        else:
            partValue = float(str(firstStr)[lessIndex + 1:])
#             print(testVec[featIndex],partValue)
            if float(testVec[featIndex]) < partValue:  # 进入左子树
                if type(secondDict['是']).__name__ == 'dict':  # 该分支不是叶子节点,递归
                    classLabel = classify_c(secondDict['是'], featLabels,
                                           featLabelProperties, testVec)
                else:  # 如果是叶子, 返回结果
                    classLabel = secondDict['是']
            else:
                if type(secondDict['否']).__name__ == 'dict':  # 该分支不是叶子节点,递归
                    classLabel = classify_c(secondDict['否'], featLabels,
                                           featLabelProperties, testVec)
                else:  # 如果是叶子, 返回结果
                    classLabel = secondDict['否']
    return classLabel
for i in range(len(testData)):
    testClass = classify_c(Trees, labels, labelProperties, testData[i])
    print("第",i,"条测试数据的预测ring为:",testClass)

f12caaccd60745bbbb9b18376aa68df3.png 与调库的方法进行对比:

参考上一篇文章:

https://blog.csdn.net/qq_53644346/article/details/125101128?spm=1001.2014.3001.55yixaiicon-default.png?t=N7T8https://blog.csdn.net/qq_53644346/article/details/125101128?spm=1001.2014.3001.5501

调库预测的结果:

39a8b96fec7f40f1950ed0edd2f5fdb5.png 发现结果一样!

总结

以上就是实现决策树算法的所有内容,当然以上算法也可以处理其他的数据集,无论是离散属性和连续属性都可以处理。

参考文章:

  • 2
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是使用Python实现西瓜数据集决策树代码,不需要调用任何库: ```python import math # 定义节点类 class Node: def __init__(self, feature=None, value=None, results=None, tb=None, fb=None): self.feature = feature # 用于划分数据集的特征 self.value = value # 特征的值 self.results = results # 存储叶子节点的分类结果 self.tb = tb # 左子树 self.fb = fb # 右子树 # 计算数据集的熵 def entropy(data): results = {} for row in data: r = row[-1] if r not in results: results[r] = 0 results[r] += 1 ent = 0.0 for r in results: p = float(results[r]) / len(data) ent -= p * math.log(p, 2) return ent # 根据特征和特征值划分数据集 def divide_data(data, feature, value): split_func = None if isinstance(value, int) or isinstance(value, float): split_func = lambda row: row[feature] >= value else: split_func = lambda row: row[feature] == value set1 = [row for row in data if split_func(row)] set2 = [row for row in data if not split_func(row)] return (set1, set2) # 选择最好的特征和特征值来划分数据集 def find_best_feature(data): best_feature = -1 best_value = None best_gain = 0.0 base_entropy = entropy(data) for feature in range(len(data[0]) - 1): feature_values = set([row[feature] for row in data]) for value in feature_values: set1, set2 = divide_data(data, feature, value) p = float(len(set1)) / len(data) gain = base_entropy - p * entropy(set1) - (1 - p) * entropy(set2) if gain > best_gain: best_feature = feature best_value = value best_gain = gain return (best_feature, best_value) # 构建决策树 def build_tree(data): if len(data) == 0: return Node() results = [row[-1] for row in data] if results.count(results[0]) == len(results): return Node(results=results[0]) best_feature, best_value = find_best_feature(data) set1, set2 = divide_data(data, best_feature, best_value) tb = build_tree(set1) fb = build_tree(set2) return Node(feature=best_feature, value=best_value, tb=tb, fb=fb) # 打印决策树 def print_tree(tree, indent=''): if tree.results is not None: print(str(tree.results)) else: print(str(tree.feature) + ':' + str(tree.value) + '? ') print(indent + 'T->', end='') print_tree(tree.tb, indent + ' ') print(indent + 'F->', end='') print_tree(tree.fb, indent + ' ') # 对新数据进行分类 def classify(tree, data): if tree.results is not None: return tree.results else: v = data[tree.feature] branch = None if isinstance(v, int) or isinstance(v, float): if v >= tree.value: branch = tree.tb else: branch = tree.fb else: if v == tree.value: branch = tree.tb else: branch = tree.fb return classify(branch, data) # 测试决策树 def test_tree(tree, test_data): correct = 0 for row in test_data: if classify(tree, row[:-1]) == row[-1]: correct += 1 accuracy = float(correct) / len(test_data) print('Accuracy: %.2f%%' % (accuracy * 100)) # 加载西瓜数据集 def load_watermelon(): data = [ [1, 1, 1, 1, 'yes'], [1, 1, 1, 0, 'yes'], [1, 0, 1, 0, 'no'], [0, 1, 0, 1, 'no'], [0, 1, 0, 0, 'no'], [0, 0, 1, 1, 'no'], [0, 1, 1, 0, 'no'], [1, 1, 0, 1, 'no'], [1, 0, 0, 0, 'no'], [0, 1, 0, 1, 'no'] ] return data # 加载西瓜数据集2 def load_watermelon2(): data = [ [0.697, 0.460, 1, 'yes'], [0.774, 0.376, 1, 'yes'], [0.634, 0.264, 1, 'yes'], [0.608, 0.318, 1, 'yes'], [0.556, 0.215, 1, 'yes'], [0.403, 0.237, 1, 'yes'], [0.481, 0.149, 1, 'yes'], [0.437, 0.211, 1, 'yes'], [0.666, 0.091, 0, 'no'], [0.243, 0.267, 0, 'no'], [0.245, 0.057, 0, 'no'], [0.343, 0.099, 0, 'no'], [0.639, 0.161, 0, 'no'], [0.657, 0.198, 0, 'no'], [0.360, 0.370, 0, 'no'], [0.593, 0.042, 0, 'no'], [0.719, 0.103, 0, 'no'] ] return data # 加载西瓜数据集3 def load_watermelon3(): data = [ ['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '是'], ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '是'], ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '是'], ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '是'], ['浅白', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '是'], ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '是'], ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '是'], ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '是'], ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '否'], ['青绿', '硬挺', '清脆', '清晰', '平坦', '硬滑', '否'], ['浅白', '硬挺', '清脆', '模糊', '平坦', '软粘', '否'], ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '否'], ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '否'], ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '否'], ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '否'], ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '否'], ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '否'] ] return data # 加载西瓜数据集4 def load_watermelon4(): data = [ ['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '是'], ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '是'], ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '是'], ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '是'], ['浅白', '稍蜷', '浊响', '清晰',

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值