[数据挖掘] 朴素贝叶斯 以及西瓜集特征工程

朴素贝叶斯的基本思想

  • 先验概率: 通过已知事实, 推论出未知事务出现的概率
    例如: 通过硬币有两面, 推论出抛一次硬币可能出现正面的概率
  • 后验概率: 通过结果, 推论出导致该结果的原因的概率
    例如: 通过硬币抛出来之后是正面, 需要你来判断该硬币被你做手法了的概率

关于先验概率 与 后验概率的理解: https://www.cnblogs.com/yemanxiaozu/p/7680761.html

  • 贝叶斯公式:
    [数据挖掘] 朴素贝叶斯 以及西瓜集特征工程
    • 其中, B 是 该事件最后的结果, A 是表示导致该结果的原因, 再结合我们之前对后验概率的理解, 可以知道, 贝叶斯公式的结果就是导致结果B出现的可能性
    • 在机器学习中, 对于一个需要预测的数据, 我们知道的是它的各种已知信息相当于公式中的A, 而我们要求的是最后对他的各个分类的概率, 这个分类就相当于公式中的 B, 于是 我们的公式可以表示为:
      [数据挖掘] 朴素贝叶斯 以及西瓜集特征工程
  • 机器学习—朴素贝叶斯: 对于上述的贝叶斯公式, 其中已知的 A 可以看作该数据的特征, 而我们假定该数据的各个特征是独立的, 所以对于p(特征|类别), 我们可以分解为 p(特征1|类别) * p(特征2|类别) * p(特征3|类别) … 这就是朴素的意思, 而对于需预测的数据来说, 我们p(特征), p(类别) 是很容易通过大量数据得到的已知值
    • 引用西瓜书上说的:
      其中ck表示第k 个分类, xj 表示第j个特征值, Y是该数据最终的分类结果
      [数据挖掘] 朴素贝叶斯 以及西瓜集特征工程
    1. 因此 我们需要训练的是什么呢:
      每个特征属于每个分类的概率
    2. 通过被预测数据的特征, 从训练出来的分类器中查找每个特征在每个分类的概率相乘(朴素)
    3. 得到该数据最终属于每个分类的概率, 选出概率最大的为其最后的分类
  • 本文算法代码只讨论二分类情况

朴素贝叶斯分类的过程

  1. 数据获取: 任意方式获取
  2. 数据分析: 得到其独立同分布的特征, 应用朴素这一条件
  3. 训练过程:
    • 若是离散值: 获取每一个特征在每一个分类下的可能性
    • 若是连续值: 假定其符合正态分布, 计算其一维正态分布值:
      [数据挖掘] 朴素贝叶斯 以及西瓜集特征工程
  4. 预测过程: 获取数据各个特征, 计算其属于每个分类的概率, 选出最大概率作为其分类结果
  5. 模型评估: 本章采用 留出法, k折交叉验证法(留一法), 自助法, 结果为
    [真正例, 假正例, 真负例, 假负例]
  6. 性能评估: 准确率

数据集:

1,青绿,蜷缩,浊响,清晰,凹陷,硬滑,0.697,0.46,是
2,乌黑,蜷缩,沉闷,清晰,凹陷,硬滑,0.774,0.376,是
3,乌黑,蜷缩,浊响,清晰,凹陷,硬滑,0.634,0.264,是
4,青绿,蜷缩,沉闷,清晰,凹陷,硬滑,0.608,0.318,是
5,浅白,蜷缩,浊响,清晰,凹陷,硬滑,0.556,0.215,是
6,青绿,稍蜷,浊响,清晰,稍凹,软粘,0.403,0.237,是
7,乌黑,稍蜷,浊响,稍糊,稍凹,软粘,0.481,0.149,是
8,乌黑,稍蜷,浊响,清晰,稍凹,硬滑,0.437,0.211,是
9,乌黑,稍蜷,沉闷,稍糊,稍凹,硬滑,0.666,0.091,否
10,青绿,硬挺,清脆,清晰,平坦,软粘,0.243,0.267,否
11,浅白,硬挺,清脆,模糊,平坦,硬滑,0.245,0.057,否
12,浅白,蜷缩,浊响,模糊,平坦,软粘,0.343,0.099,否
13,青绿,稍蜷,浊响,稍糊,凹陷,硬滑,0.639,0.161,否
14,浅白,稍蜷,沉闷,稍糊,凹陷,硬滑,0.657,0.198,否
15,乌黑,稍蜷,浊响,清晰,稍凹,软粘,0.36,0.37,否
16,浅白,蜷缩,浊响,模糊,平坦,硬滑,0.593,0.042,否
17,青绿,蜷缩,沉闷,稍糊,稍凹,硬滑,0.719,0.103,否
  • 特征包含了离散值与连续值, 最后是数据标签
  • 可看到连续值已经处于 (0,1)之间了, 因此不需要归一化处理

版本1

  • 这个版本只完成了最基础的西瓜数据集的朴素贝叶斯分类
  • 写的时候发现每次预测都要训练, 然后, 想到把训练与预测分类, 就可以多次使用分类器了, 然后有了版本2

记录一下代码有多烂:

import numpy as np
import math


def label_count(dataset):
    """
    计算每个分类结果的概率
    :param dataset:[ [feat], [feat], [feat], ... , [label], ]
    :return: p(label) : {label: prob, }
    """
    label = {}
    for i in dataset:
        if i[-1] not in label.keys():
            label[i[-1]] = 0
        label[i[-1]] += 1

    return label


def feat_count(dataset, item, label, labels):
    """
    计算某个特征向量被分类为label的概率
    :param dataset:
    :param labels 每种分类的概率
    :param item: 特征向量
    :return:
    """
    feat_num = np.array([0 for _ in item], np.float32)
    for i in range(len(item)):
        if isinstance(item[i], (float, int)):
            set_i = np.array([j[i] for j in dataset])
            u = set_i.mean()
            std = set_i.std()
            feat_num[i] = (1/(math.sqrt(2*math.pi)*std)) * math.pow(math.e, -1*(feat_num[i] - u)**2 / (2*(std**2)))
        else:
            for j in dataset:
                if item[i] == j[i] and j[-1] == label:
                    feat_num[i] += 1
            feat_num[i] /= labels[label]
    return feat_num


def classification(item, dataset):
    """
    返回属于每一种分类的可能性
    :param item:
    :param dataset:
    :return:
    """
    labels = label_count(dataset)
    data_num = len(dataset)
    ans = {}
    for label in labels:
        feat_num = feat_count(dataset, item, label, labels)
        ans[label] = 1
        for i in feat_num:
            ans[label] *= i
        ans[label] *= float(labels[label]) / float(data_num)
    return sorted(ans.items(), key=lambda j: j[1], reverse=True)


def fun(i):
        i = i.strip()
        try:
            i = float(i)
        except ValueError:
            pass
        return i


def read_data(path):
    dataset = []
    with open(path, 'r', encoding='utf8') as f:
        for line in f.readlines():
            split_ = line.strip().split(',')[1:]
            dataset.append([i for i in map(fun, split_)])
    return dataset


if __name__ == '__main__':
    path = "xigua.txt"
    dataset = read_data(path)

    pre_data = '青绿,蜷缩,浊响,清晰,凹陷,硬滑,0.697,  0.460'
    item = [i for i in pre_data.strip().split(',')]
    item = [i for i in map(fun, item)]

    print(item)
    l = classification(item, dataset)
    print(l)

结果:
[数据挖掘] 朴素贝叶斯 以及西瓜集特征工程

评估

把数据集放进去, 分出测试集与训练集, 进行评估即可
结果:
[真正例, 假正例, 真负例, 假负例]
[数据挖掘] 朴素贝叶斯 以及西瓜集特征工程

版本2

  • 这个版本把训练过程 与测试过程分开了
  • 加入了拉普拉斯假设: 即把为0的特征进行+1, 避免了该分类可能性为0
  • 最后给每个特征概率的结果相乘改成了: 最后求log后相加, 避免了特征很多的时候出现的大误差
  • 写的时候想到了是不是可以用矩阵运算来避免遍历过程, 以及面向对象.于是出现了版本3

训练

  • 训练返回结果的数据结构:
    1. 离散型, feat_pos_count, feat_neg_count, 表示分类结果为正例和负例的计数
      {
      特征1:{value1: count, value2: count},
      特征2:{value1: count, value2: count},

      }
    2. 连续型, numer_feat
      {
      连续型特征: {‘u’: 平均数, ‘std’: 标准差},

      }
    3. pos_prob: 正例占总数的可能性, 负例直接 1-pos_prob
  • 训练过程就是计数而已, 很简单
def n_attribute(u, std, x):
    return (1/(sqrt(2*pi)*std)) * pow(e, -1*(x - u)**2 / (2*(std**2)))


def train_bayesian_2_classify(dataset):
    """
    2分类器, 1--正, 0--负
    训练贝叶斯分类器, 即得到每个特征值的条件概率
    :param dataset:最后一列是label 其他作为特征值
    :return:
    """
    # 初始化每个特征有一个计数,避免最后概率为0. 拉普拉斯校准
    feat_num = len(dataset[0])-1
    feat_pos_count = [dict() for _ in range(feat_num)]
    feat_neg_count = [dict() for _ in range(feat_num)]
    # 获取正负例数
    pos_num = 0.0
    neg_num = 0.0
    data_num = len(dataset)
    numer_feat = []
    for i in range(feat_num):
        if isinstance(dataset[0][i], (float,)):
            numer_feat.append(i)

    for i in range(data_num):
        for j in range(feat_num):
            if j not in numer_feat:
                if dataset[i][j] not in feat_pos_count[j].keys():
                    feat_pos_count[j][dataset[i][j]] = 0
                    feat_neg_count[j][dataset[i][j]] = 0
                if dataset[i][-1] == 1:  # 在正例条件下,对该特征计数
                    feat_pos_count[j][dataset[i][j]] += 1
                else:
                    feat_neg_count[j][dataset[i][j]] += 1
        if dataset[i][-1] == 1:
            pos_num += 1
        else:
            neg_num += 1
    pos_prob = pos_num / float(data_num)
    for i in range(feat_num):
        if i not in numer_feat:
            for k in feat_pos_count[i]:
                if feat_pos_count[i][k] == 0:
                    feat_pos_count[i][k] = 1
                    pos_num += 1
                if feat_neg_count[i][k] == 0:
                    feat_neg_count[i][k] = 1
                    neg_num += 1
    for i in range(feat_num):
        if i not in numer_feat:
            for k in feat_pos_count[i]:
                feat_pos_count[i][k] /= float(pos_num)
                feat_neg_count[i][k] /= float(neg_num)

    # 计算标称属性的均值与方差
    for i in numer_feat:
        set_i = np.array([j[i] for j in dataset])
        u = set_i.mean()
        std = set_i.std()
        feat_pos_count[i]['u'] = u
        feat_pos_count[i]['std'] = std
        feat_neg_count[i]['u'] = u
        feat_neg_count[i]['std'] = std
    return pos_prob, feat_pos_count, feat_neg_count, numer_feat

预测:

  • 预测对于离散特征, 直接从训练结果中查询,
    对于连续型特征, 从训练结果中查询出u 与 std, 计算结果即可
  • 代码
def prediction(pos_prob, feat_pos_count, feat_neg_count, numer_feat, item):
    feat_num = len(item)
    # log 化 去除精度问题
    res_p = 0
    res_n = 0
    for i in range(feat_num):
        if i not in numer_feat:
            res_p += log2(feat_pos_count[i][item[i]])
            res_n += log2(feat_neg_count[i][item[i]])
        else:
            res_p += log2(n_attribute(feat_pos_count[i]['u'], feat_pos_count[i]['std'], item[i]))
            res_n += log2(n_attribute(feat_neg_count[i]['u'], feat_neg_count[i]['std'], item[i]))
    res_p += log2(pos_prob)
    res_n += log2(1-pos_prob)
    l = [(1, res_p,), (0, res_n)]
    l.sort(key=lambda i:i[1], reverse=True)
    return l

结果即评估:

  • 预测结果:
    [数据挖掘] 朴素贝叶斯 以及西瓜集特征工程
  • 评估结果
    [数据挖掘] 朴素贝叶斯 以及西瓜集特征工程

版本3

简介:

  • 面向对象,
    • 分类器一个对象
    • 特征工程一个对象
    • 模型评估为一个对象
  • 因为想到对离散特征每一个值都需要进行计数, 因此想到直接使用独热编码, 扩展维度: 把每个特征值当成一个新的维度
  • pandas来存储数据以及数据处理, 减少了很多遍历, 直接矩阵方法运算\
  • 哇, 写不下去了, 想到我在写这个代码的时候因为pandas用得不熟, 各种bug, 各种查资料, 大部分时间都在解决pandas的bug去了, 直接看代码吧, 这个代码的鲁棒性很大提高,应该可以用到很多数据集上了…但是, 数据集太小, 也不知道实际模型效果如何, 实在没时间弄了,

数据集特征工程

OneHotEncoder 与LabelEncoder

  • sklearn.preprocessing 里面包含了两种对离散数据的编码工具:

    • OneHotEncoder: 只能对数字进行处理, 所以在处理string数据的时候必须先转化成数字形式, 例:

      from sklearn.preprocessing import LabelEncoder
      from sklearn.preprocessing import OneHotEncoder
      enc =  OneHotEncoder()
      # 训练 传入二维数据集
      enc.fit_transform([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2],[1,1,1]])
      print(enc.n_values_)  # 打印每个特征的值的个数
      enc.transform([[0,1,3]]).toarray()
      

      结果:

        [2 3 4]
        array([[1., 0., 0., 1., 0., 0., 0., 0., 1.]])
      
      • LabelEncoder: 对离散的文本或者数字进行编号, 可以对字符串的特征向量进行处理, 可以进行逆编码: 即把编码之后的编号还原成特征值描述 inverse_transform()
  • pandas 自带的编码方法::

    • 独热编码: pd.get_dummies(), 可以对字符串进行处理, 而连续型的特征会自动忽略.
      例:

      print(dataset.head(3))
      new_dataset = pd.get_dummies(dataset)
      print(new_dataset.head(3))
      

      结果:
      [数据挖掘] 朴素贝叶斯 以及西瓜集特征工程

    • 标签编码: pd.Categorical().codes
      不知道这个方法与 sklearn的有什么不一样, 具体待查阅资料

    • 结论: 如果只需要对数据进行处理, 那么 pandas自带的方法更好用

    • 如果需要获取训练出来的编码器, 那么 sklearn会保存这个编码器, 在你对新的数据进行处理的时候, 可以直接使用该编码器得到跟训练数据一样的编码

pandas 给我弄晕了的方法:

取值

  • pd.loc[index] -->return Series
  • pd.loc[[index]] / pd.reindex([]) -->return DataFrame , 一般用后一种方法 reindex
  • pd.iloc[] 跟loc 一样, 获取某一行, 不过传入的是行的下标
  • pd.iloc[[]]
  • pd[col_name] --> , return Series 直接获取某一列, 的值
  • pd[[col_name]] --> return DataFrame
  • pd.at[index, col_name] --> 获取某一个数据点

集合

  • 并集: append

  • 差集: 比如 求 df1 与 df2 的差集

    df1.append(df2).append(df2).drop_duplicates(keep=False)
    

    即: 对其中一个元素 append两次, 然后再在最后去重, 参数表示把重复的元素都删掉, 如果 keep=True, 会保留重复元素的第一个

赋值

  • pd.at[r, c] = x
  • pd.[col_name] = Series, 如果, 这一列不存在 则添加新的一列
  • pd.loc[[index]] = Series, 给某一行赋值, 不存在这一行报错
  • DataFrame.append --> 与下面一样
  • series.append(series) --> 在series下面拼接, 不改变原元素, 返回新的Series

特殊索引 *** 我记得numpy 也是这样

  • 在df[]的中括号里面传入与 df 的列数匹配的Series, 返回里面为 True 的值
  • 看两个例子
  • >>> import pandas as pd
    >>> pd.DataFrame([[1,2,3], [2,3,4]])
       0  1  2
    0  1  2  3
    1  2  3  4
    >>> pd
    <module 'pandas' from 'D:\\software\\Python3.6\\lib\\site->packages\\pandas\\__init__.py'>
    >>> df = pd.DataFrame([[1,2,3], [2,3,4]])
    >>> s = pd.Series([True, True, False])
    >>> s
    0     True
    1     True
    2    False
    dtype: bool
    >>> df[s]
    __main__:1: UserWarning: Boolean Series key will be reindexed to match >DataFrame index.
       0  1  2
    0  1  2  3
    1  2  3  4
    >>> s = pd.Series([True,False, False])
    >>> df[s]
       0  1  2
    0  1  2  3
    >>>		
    

代码

  1. 特征工程方法
def feat_handle(dataset, dis_cols, cont_cols, v_col):
    """
    特征工程,
    :param dataset: DataFrame原始数据集 pd.DataFrame
    :param dis_cols: list 离散特征列 [clo1, col2, col3 ...]
    :param cont_cols: list 连续特征列 [col1, col2, col3]
    :param v_col: int/str 数据标签(类别) 所在的列名
    :return:
    """
    values = dataset[v_col]

    discrete_data = dataset[dis_cols]
    # print('原始数据')
    # print(dataset.head())

    enc = OneHotEncoder()
    X = enc.fit_transform(discrete_data)
    discrete_data = pd.DataFrame(X.toarray())

    # discrete_data = pd.get_dummies(discrete_data)
    # print('\n编码后')
    # print(discrete_data.head())

    continuous_data = dataset[cont_cols]
    # print('连续特征')
    # print(continuous_data.head())

    # 给最终分类结果 标签编码 1, 0:
    # values = values.apply(lambda x: pd.Categorical(x).codes)[v_col]
    le = LabelEncoder()
    Y = le.fit_transform(values)
    values = pd.Series(Y)
    # print('数据标签 y向量:')
    # print(values.head())
    return continuous_data, discrete_data, values, enc, le

  1. 二分类分类器:
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
import numpy as np
import pandas as pd


class Bayes_2:
    def __init__(self, discrete_data, continuous_data, values):
        self.dis_data = discrete_data
        self.con_data = continuous_data
        # 这个是series类型
        self.V = values
        self.feat_prob = pd.DataFrame(columns=discrete_data.keys(), index=[0, 1])
        self.con_charct_1 = pd.DataFrame(columns=continuous_data.keys(), index=['std', 'mean'])
        self.con_charct_0 = pd.DataFrame(columns=continuous_data.keys(), index=['std', 'mean'])

        self.prob_0 = 0.0
        self.prob_1 = 0.0

    def train(self):
        # 拉普拉斯校准的计数
        count1_off = 0
        count0_off = 0
        for key in self.feat_prob.keys():
            # print(self.dis_data.loc[self.dis_data[key] == 1].index)
            # print(self.V[self.V == 1].index)
            # print(self.V[self.V == 1].index & self.dis_data.loc[self.dis_data[key] == 1].index)

            self.feat_prob.at[1, key] = self.dis_data[key].reindex(self.V[self.V == 1].index & self.dis_data.loc[self.dis_data[key] == 1].index).count()

            if self.feat_prob.at[1, key] == 0:
                count1_off += 1

            self.feat_prob.at[0, key] = self.dis_data[key].reindex(self.V[self.V == 0].index & self.dis_data.loc[self.dis_data[key] == 1].index).count()
            if self.feat_prob.at[0, key] == 0:
                count0_off += 1

        # print(self.feat_prob)
        p1 = self.V[self.V == 1].count()
        count = self.V.count()
        p0 = count - p1

        self.prob_1 = p1 / float(count)
        self.prob_0 = p0 / float(count)
        self.feat_prob.loc[1] /= float(p1 + count1_off)
        self.feat_prob.loc[0] /= float(p0 + count0_off)

        # 计算连续型数据的标准差和平均值
        for key in self.con_data.keys():
            self.con_charct_1.at['std', key] = self.con_data[key].reindex(self.V[self.V == 1].index).std()
            self.con_charct_0.at['std', key] = self.con_data[key].reindex(self.V[self.V == 0].index).std()
            self.con_charct_1.at['mean', key] = self.con_data[key].reindex(self.V[self.V == 1].index).mean()
            self.con_charct_0.at['mean', key] = self.con_data[key].reindex(self.V[self.V == 0].index).mean()

    def predict(self, dis_feat, conti_feat):
        # 获取预测数据每个特征值取得的概率
        feat_1 = self.feat_prob.loc[1] * dis_feat
        feat_0 = self.feat_prob.loc[0] * dis_feat
        # log化
        feat_1 = feat_1.apply(lambda x: np.log2(np.float64(x.values)) if x.values > 0 else 0)
        feat_0 = feat_0.apply(lambda x: np.log2(np.float64(x.values)) if x.values > 0 else 0)

        # 获取预测数据离散特征值的正太分布函数值 并log化
        # print(self.con_charct_1)
        conti_feat_1 = conti_feat.apply(lambda x: self._norm_value(x, self.con_charct_1.at['std', x.name], self.con_charct_1.at['mean', x.name]))
        conti_feat_0 = conti_feat.apply(lambda x: self._norm_value(x, self.con_charct_0.at['std', x.name], self.con_charct_0.at['mean', x.name]))
        # print(conti_feat)

        # log结果 累加 相当于非log的累乘
        pre_1 = feat_1.sum() + conti_feat_1.values.sum() + np.log2(self.prob_1)
        pre_0 = feat_0.sum() + conti_feat_0.values.sum() + np.log2(self.prob_0)

        return sorted([(1, pre_1), (0, pre_0)],key=lambda x: x[1], reverse=True)

    def _norm_value(self, x, std, u):
        return (1/(np.sqrt(2*np.pi)*std)) * pow(np.e, -1*(x - u)**2 / (2*(std**2)))

    def evolut_hold_out(self, t=0.3):
        pass
  1. 评估, 这块的代码.鲁棒性不强, 不知道其他数据集上能不能用

class Evolutor:

    def __init__(self, learner, dataset, cont_cols, dis_cols, v_col):
        self.learner = learner
        self.dataset = dataset
        self.dis_cols = dis_cols
        self.cont_cols = cont_cols
        self.v_col = v_col

    def feat_handle(self):
        # 进行特征工程
        self.continuous_data, self.discrete_data, self.values, self.enc, self.le = feat_handle(self.dataset, self.dis_cols, self.cont_cols,
                                                                      self.v_col)

    def hold_out(self, t=0.3, times=1):
        self.feat_handle()
        # 真正例, 假正例, 真负例, 假付例
        assem = [0, 0, 0, 0]
        testset = list()
        trainset = list()
        for _ in range(times):
            test1num = int(len(self.values[self.values == 1]) * t)
            test0num = int(len(self.values[self.values == 0]) * t)
            testnum = test1num + test0num
            # 随机选择 testnum个数据
            test1_index = np.random.choice(self.discrete_data.iloc[self.values[self.values == 1].index].index, test1num)
            test0_index = np.random.choice(self.discrete_data.iloc[self.values[self.values == 0].index].index, test0num)
            testset.append(self.discrete_data.reindex(np.append(test1_index, test0_index)))
            testset.append(self.continuous_data.reindex(np.append(test1_index, test0_index)))
            testset.append(self.values[np.append(test1_index, test0_index)])  # 这里有 bug
            # 得到dataset与testset的差集
            tmp = self.discrete_data.append(testset[0]).append(testset[0])
            trainset.append(tmp.drop_duplicates(keep=False))
            tmp = self.continuous_data.append(testset[1]).append(testset[1])
            trainset.append(tmp.drop_duplicates(keep=False))
            trainset.append(self.values[list(filter(lambda x: x not in testset[2].index, self.values.index))])

            ## 进行贝叶斯分类

            for i in range(testnum):
                self.learner = Bayes_2(trainset[0], trainset[1], trainset[2])
                self.learner.train()
                pre_dis_v = testset[0].iloc[[i]]
                pre_cont_v = testset[1].iloc[[i]]
                res = self.learner.predict(pre_dis_v, pre_cont_v)
                # print(testset[2].iloc[i])
                # print(res)
                if testset[2].iloc[i] == '是' or testset[2].iloc[i] == 1:
                    if res[0][0] == '是' or res[0][0] == 1:
                        assem[0] += 1
                    else:
                        assem[3] += 1
                elif testset[2].iloc[i] == '否' or testset[2].iloc[i] == 0:
                    if res[0][0] == '否' or res[0][0] == 0:
                        assem[2] += 1
                    else:
                        assem[1] += 1

            # assem = [0,0,0,0]
        # assem = [i / times for i in assem]
        print('留出法,测试集比例:t={0},结果:{1}'.format(t, assem))
        return assem

    def cross_validate(self):
        """
        留一法
        :return:
        """
        # 真正例, 假正例, 真负例, 假负例
        assem = [0, 0, 0, 0]
        datanum = len(self.dataset)
        for i in range(datanum):
            item = self.dataset.iloc[[i]]
            trainset = self.dataset.iloc[:i]
            trainset = trainset.append(self.dataset.iloc[i + 1:])
            # 进行特征工程
            continuous_data, discrete_data, values, enc, le = feat_handle(self.dataset, self.dis_cols, self.cont_cols,
                                                                          self.v_col)

            self.learner = Bayes_2(discrete_data, continuous_data, values)
            self.learner.train()
            pre_dis_v = pd.DataFrame(enc.transform(item[self.dis_cols]).toarray())
            pre_cont_v = item[self.cont_cols]
            res = self.learner.predict(pre_dis_v, pre_cont_v)

            if item.iloc[0][self.v_col] == '是' or item.iloc[0][self.v_col] == 1:
                if res[0][0] == '是' or res[0][0] == 1:
                    assem[0] += 1
                else:
                    assem[3] += 1
            elif item.iloc[0][self.v_col] == '否' or item.iloc[0][self.v_col] == 0:
                if res[0][0] == '否' or res[0][0] == 0:
                    assem[2] += 1
                else:
                    assem[1] += 1
        print('留1法:{0}'.format(assem))
        return assem

    def bootstrap(self):
        assem = [0, 0, 0, 0]
        data_num = len(self.dataset)
        test_index = np.random.randint(0, data_num-1, data_num)
        testset = self.dataset.iloc[test_index]

        trainset = self.dataset.copy()
        # 进行特征工程
        continuous_data, discrete_data, values, enc, le = feat_handle(self.dataset, self.dis_cols, self.cont_cols,
                                                                      self.v_col)

        self.learner = Bayes_2(discrete_data, continuous_data, values)
        self.learner.train()

        for i in range(len(testset)):
            item = testset.iloc[[i]]
            pre_dis_v = pd.DataFrame(enc.transform(item[self.dis_cols]).toarray())
            pre_cont_v = item[self.cont_cols]
            res = self.learner.predict(pre_dis_v, pre_cont_v)

            if item.iloc[0][self.v_col] == '是' or item.iloc[0][self.v_col] == 1:
                if res[0][0] == '是' or res[0][0] == 1:
                    assem[0] += 1
                else:
                    assem[3] += 1
            elif item.iloc[0][self.v_col] == '否' or item.iloc[0][self.v_col] == 0:
                if res[0][0] == '否' or res[0][0] == 0:
                    assem[2] += 1
                else:
                    assem[1] += 1
        print('自助法:{0}'.format(assem))
        return assem

  1. 主函数
if __name__ == '__main__':
    path = r"D:\Workspace\Pyworkspase\DataMining\Bayesian\xigua.txt"
    # 数据获取
    dataset = pd.read_csv(path, ",", header=None, usecols=[i for i in range(1, 10)])
    dis_cols = [i for i in range(1, 7)]
    cont_cols = [7, 8]
    v_col = 9
    print('特征工程','-'*100)
    # 特征工程, 获取 one-hot编码器 与 label编码器, 还有数据里面的编码后的离散值与连续值向量 以及 最后每条数据的标签向量
    continuous_data, discrete_data, values, enc, le = feat_handle(dataset, dis_cols, cont_cols, v_col)
    print('-'*100)

    # 训练
    print('训练:', '-'*100)
    bayes = Bayes_2(discrete_data, continuous_data, values)
    bayes.train()
    print('-'*100)

    print('预测', '-'*100)
    # 1,青绿,蜷缩,浊响,清晰,凹陷,硬滑,0.697,0.46
    pre_data = pd.DataFrame([['1','青绿','蜷缩','浊响','清晰','凹陷','硬滑',0.697,0.46]])
    print('预测数据:', pre_data.values)
    # 给预测数据进行处理
    pre_dis_v = pd.DataFrame(enc.transform(pre_data[dis_cols]).toarray())
    pre_cont_v = pre_data[cont_cols]
    # 预测
    res = bayes.predict(pre_dis_v, pre_cont_v)
    print('预测结果:', res)
    print('-'*100)


    # 模型评估
    print('评估结果:')
    evolutor = Evolutor(None, dataset, cont_cols, dis_cols, v_col)
    evolutor.hold_out(times=1)
    evolutor.cross_validate()
    evolutor.bootstrap()
  1. 结果
特征工程 ----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
训练: ----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
预测 ----------------------------------------------------------------------------------------------------
预测数据: [['1' '青绿' '蜷缩' '浊响' '清晰' '凹陷' '硬滑' 0.697 0.46]]
预测结果: [(1, -5.644075586912031), (0, -8.912675295359993)]
----------------------------------------------------------------------------------------------------
评估结果:
留出法,测试集比例:t=0.3,结果:[2, 2, 0, 0]
留1法:[6, 3, 6, 2]
自助法:[8, 3, 5, 1]