基于条件熵的特征选择

  由于现在在做特征选择相关内容,之前没有太多编程的经验,所以初期对我来说有点难。现在通过写博客的方式来记录一下。

  本篇博客只要是针对只包含离散属性的数据集,基于条件熵来实现特征选择的python实现。由于现在还处于初学阶段,所以处理的数据相对简单,后续会不断完善,不断测试。目前还在探索包含连续值的数据集的特征选择,后续会上传相关代码。

  本篇文章的主要内容就是相关代码,有关香农熵,条件熵的基础知识在网上能找到很多详细的介绍,在此不再赘述。

从txt文件中读取数据

def Read_list(filename):
    file1 = open(filename+".txt", "r")
    list_row =file1.readlines()
    list_source = []
    for i in range(len(list_row)):
        column_list = list_row[i].strip().split(" ")  # 每一行split后是一个列表
        list_source.append(column_list)                # 在末尾追加到list_source
    for i in range(len(list_source)):  # 行数
        for j in range(len(list_source[i])):  # 列数
            list_source[i][j] = int(list_source[i][j]) # 将字符串转换为整数
    file1.close()
    return list_source
def createDataSet():
    dataSet = Read_list("dis")
    labels = ['年龄', '有工作', '有自己的房子', '信贷情况']  # 分类属性
    return dataSet, labels  # 返回数据集和分类属性

计算给定数据集的香农熵

def calcShannonEnt(dataSet):
    numEntires = len(dataSet)  # 返回数据集的行数
    labelCounts = {}  # 保存每个标签(Label)出现次数的字典l
    for featVec in dataSet:  # 对每组特征向量进行统计
        currentLabel = featVec[-1]  # 提取标签(Label)信息
        if currentLabel not in labelCounts.keys():  # 如果标签(Label)没有放入统计次数的字典,添加进去
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1  # Label计数
    #print("labelCounts:",labelCounts)
    shannonEnt = 0.0  # 经验熵(香农熵)
    for key in labelCounts:  # 计算香农熵
        prob = float(labelCounts[key]) / numEntires  # 选择该标签(Label)的概率
        shannonEnt -= prob * log(prob, 2)  # 利用公式计算
    return shannonEnt  # 返回经验熵(香农熵)

根据给定特征对数据集进行划分

"""
函数说明:按照给定特征划分数据集
Parameters:
    dataSet - 待划分的数据集
    axis - 划分数据集的特征
    value - 需要返回的特征的值
"""


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)
    #print("retDataSet:",retDataSet)
    return retDataSet  # 返回划分后的数据集

def split(dataSet, feature, value):
    """
    函数说明:根据给定的属性子集及各属性的属性值对数据集进行划分
    :param dataSet: 带划分的数据集
    :param feature: 传入的是属性列表,可包含多个属性
    :param value: 属性列表中各属性的取值
    :return:
    """

    subDataSet = dataSet
    length = len(feature)
    for i in range(0,length):
        subDataSet = splitDataSet(subDataSet,feature[i] - i ,value[i])
    return subDataSet

计算候选属性子集及相应的属性值列表

def attributeValue(dataSet,red):
    """
    函数说明:从数据集中取出属性子集的所有取值
    :param dataSet: 待处理的数据集
    :param red: 上一步所得到的约简子集
    """
    numFeature = len(dataSet[0]) - 1
    attr_list = []
    val_list = []
    for i in range(numFeature):
        red1 = red[:]
        value_list = []
        if i in red1:
            continue
        red1.append(i)
        red1.sort()
        for example in dataSet:
            value = []
            for j in red1:
                value.append(example[j])
            value_list.append(value)
        #print("加入第%d个特征后的属性值对:" % i, end="")
        #print(value_list)
        new_list = []
        for j in value_list:
            if j not in new_list:
                new_list.append(j)
        #print("所有不重复的属性值对:",new_list)
        attr_list.append(red1)
        val_list.append(new_list)
    # 返回的两个数组是多维数组,在后续的操作还需进一步考虑
    return attr_list,val_list

计算条件熵

def calcCondEnt(attribute_list, value_list):
    condEntropy = []
    for i in range(len(attribute_list)):
        newEntropy = 0.0
        for j in range(len(value_list[i])):
            feature = attribute_list[i]
            value = value_list[i][j]
            res = split(dataSet,feature, value)
            #print("划分后的数据子集为:",res)
            prob = len(res) / float(len(dataSet))  # 计算子集的概率
            newEntropy += prob * calcShannonEnt(res)
        # print("%d个属性子集的" % i, end="")
        # print("条件熵为:",newEntropy)
        condEntropy.append(newEntropy)
    return condEntropy

将进行特征选择后的数据集写入文件

def writeFile(reductData):
    output = open('out.txt', 'w', encoding='gbk')
    output.write('attr1\tattr2\tclass\n')
    for i in range(len(reductData)):
        for j in range(len(reductData[i])):
            output.write(str(reductData[i][j]))  # write函数不能写int类型的参数,所以使用str()转化
            output.write('\t\t')  # 相当于Tab一下,换一个单元格
        output.write('\n')  # 写完一行立马换行
    output.close()

特征选择的两种准则

(1)根据H(D|red) = H(D|C)来进行特征选择。这里red表示进行特征选择后得到的特征子集,C表示条件属性集

def featureSelect1(dataSet):
    allFeature, allValue = attributeValue(dataSet, [0, 1, 2])
    allEntropy = calcCondEnt(allFeature, allValue)
    allCond = allEntropy[0]
    print("整个条件属性集对决策属性集的条件熵为:", allCond)
    red = []
    flag = True
    while True:
        attribute_list, value_list = attributeValue(dataSet, red)
        print("所有可能的属性子集为:", attribute_list)
        print("各属性子集所有的取值为:", value_list)
        condEntropy = calcCondEnt(attribute_list, value_list)
        print(attribute_list, end="")
        print("的条件熵为:", condEntropy)
        for i in range(len(condEntropy)):
            if condEntropy[i] == allCond:
                attr = attribute_list[i]
                print("约简属性集为:", attr)
                reductData = []
                for data in dataSet:
                    feacvec = []
                    for j in attr:
                        feacvec.append(data[j])
                    feacvec.append(data[-1])
                    reductData.append(feacvec)
                print("reductData:", reductData)
                flag = False
                break
        if flag == False:
            break
        min_index = condEntropy.index(min(condEntropy))
        print("最小条件熵的索引为:", min_index)
        red = attribute_list[min_index]
        print("当前的约简属性集为:", red)

 

(2)根据H(D|red) = H(D|red')来进行特征选择。其中red' = red U {a},a为某个条件属性,当满足该等式时,red即为所选的特征子集

def featureSelect2(dataSet,features):
    minEntropy = 99
    red = []
    flag = True
    while True:
        attribute_list, value_list = attributeValue(dataSet, red)
        print("所有可能的属性子集为:", attribute_list)
        print("各属性子集所有的取值为:", value_list)
        condEntropy = calcCondEnt(attribute_list, value_list)
        print(attribute_list, end="")
        print("的条件熵为:", condEntropy)
        min_index = condEntropy.index(min(condEntropy))
        print("最小条件熵的索引为:", min_index)
        if min(condEntropy) == minEntropy:
            print("当前的约简属性集为:", red)
            print("所选择的属性为:", end="")
            for i in red:
                print(features[i], end=" ")
            print()
            reductData = []
            for data in dataSet:
                feacvec = []
                for j in red:
                    feacvec.append(data[j])
                feacvec.append(data[-1])
                reductData.append(feacvec)
            print("reductData:", reductData)
            writeFile(reductData)
            break
        minEntropy = min(condEntropy)
        red = attribute_list[min_index]

 

你可能感兴趣的:(基于条件熵的特征选择)