Exemplo n.º 1
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
Exemplo n.º 2
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
Exemplo n.º 3
0
def chooseBestFeature(dataSet):
    """
    :param dataSet:数据集
    :return:返回最佳特征
    """
    lenFeature = len(dataSet[0]) - 1  # 计算特征维度时去掉"类别"
    Hd = CalcEntropy(dataSet)  # 按类别,计算数据集的信息熵
    baseValue = 0.0  # 比较基准值
    bestFeature = 0  # 最佳特征
    for i in range(lenFeature):
        Hd_a = 0.0  # 被特征i固定的条件熵
        splitInfo = 0.0  # 定义分裂信息
        feature = [example[i] for example in dataSet]  # 提取每组数据的第i个特征
        feature = set(feature)  # 得到第i个特征的种类个数
        for fea in feature:
            subData = splitDataSet(dataSet, i, fea)  # 将特征i数据集按特征值fea分类
            p = float(len(subData)) / float(len(dataSet))  # 计算特征值fea在特征i集合的概率
            splitInfo -= p * log(p, 2)  # 计算分裂信息
            Hd_a += p * CalcEntropy(subData)
        Gain = Hd - Hd_a  # 计算信息增益,即数据信息熵与单个特征的条件熵的差
        if splitInfo == 0:
            bestFeature = i
        else:
            GainRatio = Gain / splitInfo  # 计算信息增益率,即信息增益与分裂信息之比
            # print(i, 'GainRatio=', GainRatio)
            if GainRatio > baseValue:  # 将信息增益与比较基准量baseValue比较
                baseValue = GainRatio
                bestFeature = i  # 根据增益率选择最佳特征
    return bestFeature
Exemplo n.º 4
0
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
Exemplo n.º 5
0
def chooseBestFeature(dataSet):
    """
    :param dataSet:数据集
    :return:返回最佳特征
    """
    lenFeature = len(dataSet[0]) - 1  # 计算特征维度时去掉"类别"
    Hd = CalcEntropy(dataSet)  # 按类别,计算数据集的信息熵
    baseValue = 0.0  # 比较基准值
    bestFeature = 0  # 最佳特征
    for i in range(lenFeature):
        Hd_a = 0.0  # 被特征i固定的条件熵
        feature = [example[i] for example in dataSet]  # 提取每组数据的第i个特征
        feature = set(feature)  # 得到第i个特征的种类个数
        for fea in feature:
            subData = splitDataSet(dataSet, i, fea)  # 将特征i数据集按特征值fea分类
            p = float(len(subData)) / float(len(dataSet))  # 计算特征值fea在特征i集合的概率
            # print('p = ',p,'i = ',i,'fea = ',fea)
            # 在特征i集合中,特征值fea数据的概率与信息熵乘积相加,即为被特征i固定的条件熵
            Hd_a += p * CalcEntropy(subData)
        Gain = Hd - Hd_a  # 计算信息增益,即数据信息熵与单个特征的条件熵的差
        # print(i, 'Gain=', Gain)
        if Gain > baseValue:  # 将信息增益与比较基准量baseValue比较
            baseValue = Gain
            bestFeature = i  # 根据增益选择最佳特征
    return bestFeature
Exemplo n.º 6
0
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)  # 那么停止划分,返回最多的类别
Exemplo n.º 7
0
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
Exemplo n.º 8
0
def branLapError(branch, dataSet, labelIndex, k):
    """
    :param branch:子树分支集合
    :param dataSet:数据集
    :param labelIndex:类索引值
    :return:误差加权和
    """
    ErTt = []  # 每个分支误差初值
    nTt = []  # 每个分支样本总数初值
    for keys in branch.keys():  # 计算每个分支的误差和数目
        items = keys.split(',')  # 如果该键包含多个特征值,那么进行分离
        subDataSet = splitDataSet(dataSet, labelIndex, items)
        # for data in subDataSet:
        #     print(data)
        ErTt.append(nodeLapError(subDataSet, k))
        nTt.append(len(subDataSet))
    # print('ErTt=', ErTt)
    Sum_ErTt = np.dot(ErTt, nTt) / sum(nTt)  # 计算所有分支误差的加权和
    return Sum_ErTt
Exemplo n.º 9
0
def chooseBestFeature(dataSet):
    """
    :param dataSet:数据集
    :return:返回最佳特征,最佳特征值组合
    """
    lenFeature = len(dataSet[0]) - 1  # 计算特征维度时去掉"类别"
    featureBase = 1.0  # 基尼系数比较基准值
    bestFeature = 0  # 最佳特征
    bestFeatureTuple = []  # 最佳特征二分组合
    for i in range(lenFeature):
        GiniD_A = 0.0  # 被特征i固定的基尼系数
        valueBase = 1.0  # 该特征下某个特征比较基准值
        feature = [example[i] for example in dataSet]  # 提取每组数据的第i个特征
        feature = set(feature)  # 得到第i个特征的种类个数
        # print('feature=', feature)
        if len(feature) == 1:  # 如果该特征只有一个特征值
            bestValueTuple = [feature]  # 不需要划分特征组合
            GiniD_A = 1.0
        else:
            featureGroup = featureSplit(feature)  # 将该特征划分为二分序列组合
            for valueTuple in featureGroup:
                GiniD_a = 0.0  # 某个特征下某个特征值的基尼系数
                for value in valueTuple:
                    subData = splitDataSet(dataSet, i,
                                           value)  # 将特征i数据集按特征值value分类
                    p = float(len(subData)) / float(
                        len(dataSet))  # 计算特征值value在特征i集合的概率
                    # 在二分序列组合中,将每个特征值value概率与基尼系数乘积再相加,即为该组合基尼系数
                    GiniD_a += p * CalcGini(subData)
                # print('valueTuple=', valueTuple)
                # print('GiniD_a=', GiniD_a)
                if GiniD_a < valueBase:  # 将基尼系数与比较基准量valueBase比较
                    valueBase = GiniD_a
                    GiniD_A = GiniD_a  # 特征值组合最小的基尼系数,即为该特征的基尼系数
                    bestValueTuple = valueTuple  # 记录该最佳特征值组合
        bestFeatureTuple.append(bestValueTuple)  # 将所有特征值组合构成集合
        # print('bestFeatureTuple=', bestFeatureTuple)
        # print('GiniD_A=', GiniD_A)
        if GiniD_A < featureBase:  # 根据基尼系数选出最佳特征
            featureBase = GiniD_A
            bestFeature = i
    return bestFeature, bestFeatureTuple[bestFeature]  # 返回最佳特征,以及该特征下最佳特征值组合
Exemplo n.º 10
0
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
Exemplo n.º 11
0
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)
Exemplo n.º 12
0
def calErrorRatio(Tree, labels, dataSet, NT, infoSet):
    """
    :param Tree:决策树
    :param labels:特征类别属性
    :param dataSet:数据集
    :param NT:数据集总样本数目
    :param infoSet:所有节点的信息总集合
    :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():  # 遍历第二个字典的键
        if type(secondDict[keys]).__name__ == 'dict':
            items = keys.split(',')  # 如果该键包含多个特征值,那么进行分离
            subDataSet = splitDataSet(dataSet, labelIndex, items)  # 划分数据集
            info, infoSet = calErrorRatio(secondDict[keys], subLabels,
                                          subDataSet, NT, infoSet)
            info.setdefault('keys', keys)  # 在节点信息集中,增加分类前特征
            infoSet.append(info)
    # print('=============================')
    # print('Tree=', Tree)
    # print('firstFeat=', firstFeat)
    # print('secondDict=', secondDict)
    Rt = nodeError(dataSet) / NT  # 计算节点误差率
    RTt = leafError(Tree, labels, dataSet) / NT  # 计算子树误差率
    Nt = getNumLeaf(Tree)  # 计算叶节点数目
    if Nt == 1:
        a = 2.0
    else:
        a = (Rt - RTt) / (Nt - 1)  # 计算误差增加率
    info = {'Tree': Tree, 'NumLeaf': Nt, 'a': a}  # 构建节点信息集
    # print('info=', info)
    return info, infoSet
Exemplo n.º 13
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