数据预处理——数据离散化处理

数据离散化
数据离散化(也叫数据分组)是指将连续的数据进行分组,使其变为一段段离散化的区间。
根据离散化过程中是否考虑类别属性,可以将离散化算法分为有监督算法和无监督算法两类。由于有监督算法(如基于信息熵进行数据的离散化)充分利用了类别属性的信息,所以在分类中能获得较高的正确率。
以下介绍的数据分组方法均需要对数据进行排序,且假设待离散化的数据按升序排列。
1.等宽分组
等宽分组的原理是:根据分组的个数得出固定的宽度,分到每个组中的变量的宽度是相等的。
例如,将一组变量(1,7,12,12,22,30,34,38,46)分成三组
(1)宽度为15,即用变量中的最大值(46)减去变量中最小的值(1),然后用差除以组数3.
(2)每组的范围为:(1,16]、(19,31]、(31,46]
(3)分组后的结果为:(1,7,12,12)、(22,30)、(34,38,46)
2.等频分组
等频分组也叫分位数分组,即分组后每组的变量个数相同。
将上面变量分组,得到结果为(1,7,12)、(12,22,30)、(34,38,46)
注意:
等宽分组和等频分组实现起来比较简单,但都需要人为地指定分组个数。
等宽分组的缺点是:对离群值比较敏感,将属性值不均匀地分布到各个区间。有些区间包含的变量较多,有些区间包含的变量较少。
等频分组虽然能避免等宽分组的缺点,但是会将相同的变量值分到不同的组(以满足每个组内的变量个数相同)
3.单变量分组
单变量分组也叫秩分组。其原理是:将所有变量按照降序或升序,排序名次即为排序结果,即将值相同的变量划分到一组。
将上述分组结果为:(1)、(7)、(12,12)、(22)、(30)、(34)、(38)、(46)
4.基于信息熵分组
先说说信息量和熵的概念。
(1)信息量
香农(Shannon)被称为“信息论之父”,他被认为“信息是用来消除随机不确定性的东西”。即,衡量信息量大小就看这个信息消除不确定性的程度。
信息量的大小和事件发生的概率成反比。信息量可表示为:
信息量度量的是“一个具体事件发生”所带来的信息。

(2)熵
熵是在结果出来之前对可能产生的信息量的期望——考虑该随机变量的所有可能取值,即所有可能发生事件所带来的信息量的期望。

表示事件发生的概率,n为x中所有类别的个数。
按照随机变量的所有可能取值划分数据的总熵E是所有事件的熵的加权平均:

式中,是第x个事件出现的比例,是第i个可能取值出现的次数,m是所有取值出现的总次数。
注意:
熵表示样本集合的不确定性。熵越大,则样本的不确定性越大。
基于信息熵进行数据分组的具体做法是:
(1)对属性A的所有取值从小到大排序;
(2)遍历属性A的每一个值,将属性A的值分为两个区间,使得将其作为分隔点划分数据集后的熵S最小。
(3)当划分后的熵大于设置的阈(yu,四声)值且小于指定的数据个数时,递归对S1,S2执行步骤(2)中的划分。
数据如下:

数据预处理——数据离散化处理_第1张图片

# -*-coding:utf-8-*-


import numpy as np
import math


class DiscreteByEntropy:
    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

    # 该步骤是计算数据的信息熵,是为下一步分割数据集做准备。

    # 计算按照数据指定数据分组后的香农熵
    def calEntropy(self, data):
        numData = len(data)
        labelCounts = {}
        for feature in data:
            # 获得标签
            oneLabel = feature[-1]
            # 如果标签不在新定义的字典里创建该标签值
            labelCounts.setdefault(oneLabel, 0)
            # 该类标签下含有数据的个数
            labelCounts[oneLabel] += 1
        shannonEnt = 0.0
        for key in labelCounts:
            # 同类标签出现的概率
            prob = float(labelCounts[key]) / numData
            # 以2为底求对数
            shannonEnt -= prob * math.log(prob, 2)
        return shannonEnt

    # 寻找一组数据最佳分割点的方法是:遍历所有属性值,数据按照该属性分割,使得平均熵最小。

    # 按照调和信息熵最小化原则分割数据集
    def split(self, data):
        # inf为正无穷大
        minEntropy = np.inf
        # 记录最终分割索引
        index = -1
        # 按照第一列对数据进行升序排序
        sortData = data[np.argsort(data[:, 0])]
        # 初始化最终分割数据后的熵
        lastE1, lastE2 = -1, -1
        # 返回的数据结构,包含数据和对应的熵
        S1 = dict()
        S2 = dict()
        for i in range(len(sortData)):
            # 分割数据集
            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 = DiscreteByEntropy(group=6, threshold=0.5)
    data = dbe.loadData()
    dbe.train(data)
    print("result is {}".format(dbe.result))

你可能感兴趣的:(Python,数据分析)