數據離散化與Python實現


一、原理
        數據離散化(也稱,數據分組),指將連續的數據進行分組,使其變為一段離散化的區間。

        根據離散化過程中是否考慮類別屬性,可以將離散化算法分為:有監督算法無監督算法。事實證明,由於有監督算法充分利用了類別屬性的信息,所以再分類中能獲得較高的正確率。

常用的數據離散化方法:

  • 等寬分組
  • 等頻分組
  • 單變量分組
  • 基於信息熵分組

        數據離散化所使用的方法需要事先對數據進行排序,且假設待離散化的數據是按照升序排序。

1、等寬分組
        原理:根據分組的個數得出固定的寬度,分到每個組中的變量的寬度是相等的。

如:現在有一個待離散化的數組[1, 7, 12, 12, 22, 30, 34, 38, 46],需要分成三組,

那么,width = \frac{x_{max} - x_{min}}{n},即寬度 =( 46 - 1)/3 = 15

 分組后結果范圍:[1,16],(16, 31],(31, 46],第一個分組取的是全閉區間,

分組后結果:[1, 7, 12, 12],[22, 30],[34, 38, 46]

2、等頻分組
        原理:等頻分組也叫分位數分組,即分組后,每個分組的元素個數是一樣的。

如:現在有一個待離散化的數組[1, 7, 12, 12, 22, 30, 34, 38, 46],需要分成三組,

那么,num = \frac{len_{arr}}{n},即每組元素的個數 = 9 / 3 = 3

分組后的結果:[1, 7, 12],[12, 22, 30],  [34, 38, 46]

3、單變量分組
        原理:單變量分組,也叫秩分組。將所有元素按照降序或者升序排序,排序名次即為排序結果,即將相同的元素划分到同一個組。

如:現在有一個待離散化的數組[1, 7, 12, 12, 22, 30, 34, 38, 46],

分組后的結果:[1], [7], [12, 12], [22], [30], [34], [38], [46]

4、基於信息熵分組
        概念:

(1)信息量

        Shannon認為,信息是用來消除隨機不確定性的東西。即,衡量信息量大小就看這個消息消除不確定性的程度。

        信息量的大小和事件發生的概率成反比。可以用公式表示為:l(x) = -log_{2}p(x)

式中,p(x)表示x發生的概率。

(2)熵

        熵,是在結果出來之前對可能產生的信息量的期望——考慮該隨機變量的所有可能取值,即所有可能發生事件所帶來的信息量的期望。

可以表示為:E(x) = -\sum_{i=1}^{n}p(x_i)log_{2}p(x_i)

        按照隨機變量的所有可能取值划分數據的總熵E是所有事件的熵的加權平均:E = \sum_{i=1}^{k}w_iE_i

式中,w_i = \frac{m_i}{m}是第x個事件出現的比例,是第個可能取值出現的次數,是所有取值出現的總次數。

熵表示的是樣本集合的不確定性。熵越大,則樣本的不確定性越大。

所以,基於信息熵進行數據分組的具體做法是:

  1. 對屬性A的所有取值從小到大進行排序;
  2. 遍歷屬性A的每個值,將屬性A的值分為兩個區間、,使得將其作為分隔點划分數據集后的熵最小;
  3. 當划分后的熵大於設置的閾值且小於指定的數據分組個數時,遞歸對、執行步驟2中的划分。

總結:

        上述分組方法中,等寬分組和等頻分組實現起來比較簡單,但需要人為指定分組個數。

        等寬分組的缺點:對離散值比較敏感,將屬性值不均勻地分布到各個區間。有些區間的元素個數較多,有些則較少,容易導致數據傾斜。

        等頻分組雖然能避免等寬分組的缺點,但是會將相同的元素分到不同的組,如例子中的“12”元素。

二、基於信息熵的數據離散化實現

import numpy as np import math class DiscreateByEntropy: def __init__(self, group, threshold): self.maxGroup = group # 最大分組數
        self.minInfoThreshold = threshold # 停止划分的最小熵
        self.result = dict() def loadData(self): data = np.array( [ [56,1],[87,1],[129,0],[23,0],[342,1], [641,1],[63,0],[2764,1],[2323,0],[453,1], [10,1],[9,0],[88,1],[222,0],[97,0], [2398,1],[592,1],[561,1],[764,0],[121,1] ] ) return data # 計算按照數據指定數據分組后的Shannon熵
    def calEntropy(self, data): numData = len(data) labelCounts = {} for feature in data: # 獲得標簽,這里只有0或者1
            oneLabel = feature[-1] # 設置字典中,標簽的默認值
            if labelCounts.get(oneLabel,-1) == -1: labelCounts[oneLabel] = 0 # 統計同類標簽的數量
            labelCounts[oneLabel] += 1 shannoEnt = 0.0
        for key in labelCounts: # 同類標簽出現的概率,某一標簽出現的次數除以所有標簽的數量
            prob = float(labelCounts[key])/numData # 求熵,以2為底,取對數
            shannoEnt -= prob * math.log2(prob) return shannoEnt # 按照調和信息熵最小化原則分割數據集
    def split(self, data): # inf為正無窮
        minEntropy = np.inf # 記錄最終分割的索引
        index = -1
        # 按照第一列對數據進行排序
        sortData = data[np.argsort(data[:,0])] # print(sortData)
        # 初始化最終分割數據后的熵
        lastE1,lastE2 = -1, -1
        # 返回的數據區間,包括數據和對應的熵
        S1 = dict() S2 = dict() for i in range(len(data)): splitData1, splitData2 = sortData[:i+1], sortData[i+1:] # 計算信息熵
            entropy1, entropy2 = ( self.calEntropy(splitData1), self.calEntropy(splitData2) ) # 計算調和平均熵
            entropy = entropy1 * len(splitData1) / len(sortData) + entropy2 * len(splitData2) / len(sortData) if entropy < minEntropy: minEntropy = entropy index = i lastE1 = entropy1 lastE2 = entropy2 S1["entropy"] = lastE1 S1["data"] = sortData[:index+1] S2["entropy"] = lastE2 S2["data"] = sortData[index+1:] return S1, S2, entropy def train(self,data): # 需要遍歷的key
        needSplitKey = [0] self.result.setdefault(0,{}) self.result[0]["entropy"] = np.inf self.result[0]["data"] = data group = 1
        for key in needSplitKey: S1, S2, entropy = self.split(self.result[key]["data"]) if entropy > self.minInfoThreshold and group < self.maxGroup: self.result[key] = S1 newKey = max(self.result.keys()) + 1 self.result[newKey] = S2 needSplitKey.extend([key]) needSplitKey.extend([newKey]) group += 1
            else: break
 
if __name__ == '__main__': dbe = DiscreateByEntropy(group=6,threshold=0.5) data = dbe.loadData() dbe.train(data) print("result is {}".format(dbe.result)) 

運行結果:


    可見,將商品價格分為了5份,下標分別對應了0,1,2,3,4.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM