Esempio n. 1
0
def createCARTTree(dataSet, labels):
    """
    :param dataSet: 数据集
    :param labels: 类别标签
    :return:决策树
    """
    classList = [example[-1] for example in dataSet]  # classList是数据集的所有类别

    # 情况1:若classList只有一类,则停止划分
    if classList.count(classList[0]) == len(classList):
        return classList[0]

    # 情况2:若完成所有的特征分类,返回个数最多的
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)  # 投票选出最多的特征

    # 情况3:classList有多类,开始进行分类
    feaBest, feature = chooseBestFeature(dataSet)  # 选择最佳分类特征
    # print('=====================================')
    # print('feaBest=', feaBest, 'feature', feature)
    newLabel = labels[feaBest]  # 得到该特征的名称
    del (labels[feaBest])  # 删掉已分类的特征名称
    Tree = {newLabel: {}}  # 创建一个多重字典,存储决策树分类结果
    for value in feature:
        subLabels = labels[:]  # 构建特征名称子集合
        strValue = ','.join(value)
        # 利用递归函数不断创建分支,直到所有特征完成分类,分类结束
        subData = splitDataSet(dataSet, feaBest, value)
        # print('---------------------------------------')
        # print('value=', value)
        Tree[newLabel][strValue] = createCARTTree(subData, subLabels)
    return Tree
def prunBranch(Tree, labels, infoBran, dataSet):
    """
    :param Tree:决策树
    :param labels:特征类别属性
    :param infoBran:需剪掉的支树信息集
    :param dataSet:数据集
    :return:剪枝后的决策树
    """
    firstFeat = list(Tree.keys())[0]  # 取出tree的第一个键名
    secondDict = Tree[firstFeat]  # 取出tree第一个键值
    labelIndex = labels.index(firstFeat)  # 找到键名在特征属性的索引值
    subLabels = labels[:labelIndex]  # 剔除预处理的键名
    subLabels.extend(labels[labelIndex + 1:])
    for keys in secondDict.keys():  # 遍历第二个字典的键
        items = keys.split(',')  # 如果该键包含多个特征值,那么进行分离
        subDataSet = splitDataSet(dataSet, labelIndex, items)  # 划分数据集
        classList = [example[-1] for example in subDataSet]
        majorClass = majorityCnt(classList)  # 找到数量最多的类别
        # 如果当前支树分类前特征和支树都和预处理相同,则把该支树剪掉
        if keys == infoBran['keys'] and secondDict[keys] == infoBran['Tree']:
            secondDict[keys] = majorClass  # 剪掉支树,即返回最大类
            return Tree
        elif type(secondDict[keys]).__name__ == 'dict':  # 如果不相同,继续向下寻找
            secondDict[keys] = prunBranch(secondDict[keys], subLabels,
                                          infoBran, subDataSet)
    return Tree
Esempio n. 3
0
def createC4_5Tree(dataSet, labels):
    """
    :param dataSet: 数据集
    :param labels: 类别标签
    :return:决策树
    """
    classList = [example[-1] for example in dataSet]  # classList是数据集的所有类别

    # 情况1:若classList只有一类,则停止划分
    if classList.count(classList[0]) == len(classList):
        return classList[0]

    # 情况2:若完成所有的特征分类,返回个数最多的
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)  # 投票选出最多的特征

    # 情况3:classList有多类,开始进行分类
    feaBest = chooseBestFeature(dataSet)  # 选择最佳分类特征
    feature = [example[feaBest] for example in dataSet]  # 提取每组数据的第i个特征
    feature = set(feature)  # 得到第i个特征的种类个数
    newLabel = labels[feaBest]  # 得到该特征的名称
    del (labels[feaBest])  # 删掉已分类的特征名称
    Tree = {newLabel: {}}  # 创建一个多重字典,存储决策树分类结果
    for value in feature:
        subLabels = labels[:]  # 构建特征名称子集合
        # 利用递归函数不断创建分支,直到所有特征完成分类,分类结束
        Tree[newLabel][value] = createC4_5Tree(
            splitDataSet(dataSet, feaBest, value), subLabels)
    return Tree
def PrePruning(trainSet, testSet, labels):
    """
    :param trainSet: 训练集
    :param testSet: 测试集
    :param labels: 类别标签
    :return: 剪枝后的决策树
    """
    classList = [example[-1] for example in trainSet]  # classList是训练集的所有类别

    # 情况1:若classList只有一类,则停止划分
    if classList.count(classList[0]) == len(classList):
        return classList[0]

    # 情况2:若完成所有的特征分类,返回个数最多的
    if len(trainSet[0]) == 1:
        return majorityCnt(classList)  # 投票选出最多的特征

    # 情况3:classList有多类,开始进行分类
    feaBest = chooseBestFeature(trainSet)  # 选择最佳分类特征
    feature = [example[feaBest] for example in trainSet]  # 提取每组数据的第i个特征
    feature = set(feature)  # 得到第i个特征的种类个数
    # print('feaBest=', labels[feaBest])
    # print('feature=', feature)
    newLabel = labels[feaBest]  # 得到该特征的名称
    labelIndex = labels.index(newLabel)  # 获取该特征在原特征集合的索引值
    beforeAccuracy = testMajor(trainSet, testSet)  # 划分前样本集精度
    afterAccuracy = testCreate(trainSet, testSet, feaBest,
                               labelIndex)  # 划分后样本集精度
    # print('beforeAccuracy=', beforeAccuracy)
    # print('afterAccuracy=', afterAccuracy)
    del (labels[feaBest])  # 删掉已分类的特征名称
    Tree = {newLabel: {}}  # 创建一个多重字典,存储决策树分类结果
    if afterAccuracy > beforeAccuracy:  # 如果划分后验证集精度比划分前高
        for value in feature:  # 那么该特征可以划分,构造决策树
            # print('==============================')
            # print('value=', value)
            subLabels = labels[:]  # 构建特征名称子集合
            # 利用递归函数不断创建分支,直到所有特征完成分类,分类结束
            subTrainSet = splitDataSet(trainSet, feaBest, value)  # 按对应特征,划分训练集
            subTestSet = splitDataSet(testSet, feaBest, value)  # 按对应特征,划分测试集
            Tree[newLabel][value] = PrePruning(subTrainSet, subTestSet,
                                               subLabels)
        return Tree
    else:  # 如果划分后没有划分前精度高
        return majorityCnt(classList)  # 那么停止划分,返回最多的类别
def PostPruning_REP(trainSet, testSet, labels):
    """
    :param trainSet:训练集
    :param testSet:测试集
    :param labels:类别属性集
    :return:剪枝后的决策树
    """
    classList = [example[-1] for example in trainSet]  # classList是训练集的所有类别

    # 情况1:若classList只有一类,则停止划分
    if classList.count(classList[0]) == len(classList):
        return classList[0]

    # 情况2:若完成所有的特征分类,返回个数最多的
    if len(trainSet[0]) == 1:
        return majorityCnt(classList)  # 投票选出最多的特征

    # 情况3:classList有多类,开始进行分类
    feaBest = chooseBestFeature(trainSet)  # 选择最佳分类特征
    feature = [example[feaBest] for example in trainSet]  # 提取每组数据的第i个特征
    feature = set(feature)  # 得到第i个特征的种类个数
    newLabel = labels[feaBest]  # 得到该特征的名称
    del (labels[feaBest])  # 删掉已分类的特征名称
    Tree = {newLabel: {}}  # 创建一个多重字典,存储决策树分类结果
    for value in feature:  # 那么该特征可以划分,构造决策树
        subLabels = labels[:]  # 构建特征名称子集合
        # 利用递归函数不断创建分支,直到所有特征完成分类,分类结束
        subTrainSet = splitDataSet(trainSet, feaBest, value)  # 按对应特征,划分训练集
        subTestSet = splitDataSet(testSet, feaBest, value)  # 按对应特征,划分测试集
        Tree[newLabel][value] = PostPruning_REP(subTrainSet, subTestSet,
                                                subLabels)
    # print('=============================')
    # print('Tree=', Tree)
    labels.insert(feaBest, newLabel)  # 为了使用决策树分类,将删掉的类别还原
    beforeAccuracy = testPrun(testSet, Tree, labels)  # 计算剪枝前测试集精度
    afterAccuracy = testMajor(trainSet, testSet)  # 计算剪枝后测试集精度
    # print('beforeAccuracy=', beforeAccuracy)
    # print('afterAccuracy=', afterAccuracy)
    del (labels[feaBest])  # 再将分类好的类别删掉
    if afterAccuracy > beforeAccuracy:  # 如果剪枝后精度比剪枝前大,那么进行剪枝
        return majorityCnt(classList)
    else:  # 否则不剪枝,返回原决策树
        return Tree
def nodeError(dataSet):
    """
    :param dataSet:数据集
    :return:误差
    """
    error = 0.0
    classList = [example[-1] for example in dataSet]
    majorClass = majorityCnt(classList)  # 找到数量最多的类别
    for i in range(len(dataSet)):  # 游历数据集每个元素,找出正确样本个数
        if dataSet[i][-1] != majorClass:
            error += 1  # 如果不一致,错误加1
    return float(error)
def testMajor(trainSet, testSet):
    """
    :param trainSet:训练集
    :param testSet:测试集
    :return:验证集精度
    """
    classList = [example[-1] for example in trainSet]
    majorClass = majorityCnt(classList)  # 找到数量最多的类别
    # print('majorClass=', majorClass)
    accuracy = 0.0
    for i in range(len(testSet)):  # 游历测试集每个元素,找出正确样本个数
        if testSet[i][-1] == majorClass:
            accuracy += 1
    return float(accuracy)
def PostPruning_PEP(Tree, labels, dataSet):
    """
    :param Tree:预处理的决策树
    :param labels:特征类别属性
    :param dataSet:数据集
    :return:剪枝后的决策树
    """
    classList = [example[-1] for example in dataSet]
    majorClass = majorityCnt(classList)  # 找到数量最多的类别
    et = nodeError(dataSet) + 1 / 2  # 计算非叶节点t误差
    Nt = getNumLeaf(Tree)  # 子树Tt叶节点数目
    eTt = leafError(Tree, labels, dataSet) + Nt / 2  # 子树Tt所有叶节点误差
    nt = len(dataSet)  # 节点t训练实例数目
    if nt > eTt:
        SeTt = np.sqrt(eTt * (nt - eTt) / nt)  # 子树Tt总误差
    else:
        SeTt = 0
    # print('================================')
    # print('Tree=', Tree)
    # print('et=', et)
    # print('eTt=', eTt)
    # print('Nt=', Nt)
    # print('nt=', nt)
    #print('SeTt=', SeTt)
    # print('eTt + SeTt=', eTt + SeTt)
    if et < eTt + SeTt:  # 若节点t误差小于子树Tt误差
        return majorClass  # 则进行剪枝,直接返回最大类
    firstFeat = list(Tree.keys())[0]  # 取出tree的第一个键名
    secondDict = Tree[firstFeat]  # 取出tree第一个键值
    labelIndex = labels.index(firstFeat)  # 找到键名在特征属性的索引值
    # print('firstFeat=', firstFeat)
    # print('secondDict=', secondDict)
    subLabels = labels[:labelIndex]  # 剔除预处理的键名
    subLabels.extend(labels[labelIndex + 1:])
    for keys in secondDict.keys():  # 遍历第二个字典的键
        if type(secondDict[keys]).__name__ == 'dict':
            items = keys.split(',')  # 如果该键包含多个特征值,那么进行分离
            # print('items=', items)
            subDataSet = splitDataSet(dataSet, labelIndex, items)  # 划分数据集
            secondDict[keys] = PostPruning_PEP(secondDict[keys], subLabels,
                                               subDataSet)
    return Tree
def testCreate(trainSet, testSet, feaBest, labelIndex):
    """
    :param trainSet:训练集
    :param testSet:测试集
    :param feaBest:最佳分类特征
    :param labelIndex:该特征在原集合的索引值
    :return:验证集精度
    """
    accuracy = 0.0
    feature = [example[feaBest] for example in trainSet]  # 提取每组数据的最佳分类特征
    feature = set(feature)  # 得到该特征的种类集合
    for value in feature:  # 游历该特征下每个属性
        # print('value=', value)
        subTrainSet = splitDataSet(trainSet, feaBest, value)
        subClass = [example[-1] for example in subTrainSet]
        majorSub = majorityCnt(subClass)  # 统计得到最大类别
        # print('majorSub=', majorSub)
        for data in testSet:  # 游历测试集每个元素
            if data[labelIndex] == value:  # 对应特征下特征属性相同
                if data[-1] == majorSub:  # 如果类别与对应最大类别相同,则样本正确
                    accuracy += 1
    return float(accuracy)
Esempio n. 10
0
def PostPruning_IMEP(Tree, labels, dataSet, m):
    """
    :param Tree:预处理的决策树
    :param labels:特征类别属性
    :param dataSet:数据集
    :param m:先验概率对后验概率的影响因子
    :return:剪枝后的决策树
    """
    classList = [example[-1] for example in dataSet]
    majorClass = majorityCnt(classList)  # 找到数量最多的类别
    firstFeat = list(Tree.keys())[0]  # 取出tree的第一个键名
    secondDict = Tree[firstFeat]  # 取出tree第一个键值
    labelIndex = labels.index(firstFeat)  # 找到键名在特征属性的索引值
    subLabels = labels[:labelIndex]  # 剔除预处理的键名
    subLabels.extend(labels[labelIndex + 1:])
    # print('======================')
    # print('Tree=', Tree)
    # print('firstFeat=', firstFeat)
    for keys in secondDict.keys():  # 遍历第二个字典的键
        # print('keys=', keys)
        if type(secondDict[keys]).__name__ == 'dict':
            items = keys.split(',')  # 如果该键包含多个特征值,那么进行分离
            # print('items=', items)
            subDataSet = splitDataSet(dataSet, labelIndex, items)  # 划分数据集
            secondDict[keys] = PostPruning_IMEP(secondDict[keys], subLabels,
                                                subDataSet, m)
    # print('--------------------')
    Ert = newNodeLapError(dataSet, m)  # 计算非叶节点的误差
    Sum_ErTt = newBranLapError(secondDict, dataSet, labelIndex,
                               m)  # 计算节点t分支的误差加权和
    # print('Ert=', Ert)
    # print('Sum_ErTt=', Sum_ErTt)
    if Ert > Sum_ErTt:  # 如果节点误差大于分支误差和,则子树保留
        return Tree
    else:  # 否则进行剪枝,返回主类
        return majorClass