由于现在在做特征选择相关内容,之前没有太多编程的经验,所以初期对我来说有点难。现在通过写博客的方式来记录一下。
本篇博客只要是针对只包含离散属性的数据集,基于条件熵来实现特征选择的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]