由於現在在做特徵選擇相關內容,之前沒有太多程式設計的經驗,所以初期對我來說有點難。現在通過寫部落格的方式來記錄一下。
本篇部落格只要是針對只包含離散屬性的資料集,基於條件熵來實現特徵選擇的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]