基於條件熵的特徵選擇

愛吃甜品的小朋友發表於2020-08-09

  由於現在在做特徵選擇相關內容,之前沒有太多程式設計的經驗,所以初期對我來說有點難。現在通過寫部落格的方式來記錄一下。

  本篇部落格只要是針對只包含離散屬性的資料集,基於條件熵來實現特徵選擇的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]

 

相關文章