• 数据离散化与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.

  • 相关阅读:
    12.Django与ajax
    11.Django的分页器paginator
    10.中间键Middleware
    09.用户认证auth模块
    08.form组件
    07.会话跟踪技术cookie与session
    06.orm模型层
    05.Django模板层
    04.Django视图函数
    03.DjangoURL路由
  • 原文地址:https://www.cnblogs.com/SysoCjs/p/11595584.html
Copyright © 2020-2023  润新知