1 算法介绍

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。

最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBM)。和决策树模型相比,朴素贝叶斯分类器(Naive Bayes Classifier 或 NBC)发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率。同时,NBC模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。理论上,NBC模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为NBC模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,这给NBC模型的正确分类带来了一定影响。

朴素贝叶斯公式如下:

P(A_{i}|B) = \frac{P(B|A_{i})P(A_{i})}{\sum_{j}^{}P(B|A_{j})P(A_{j})}

这个是什么意思呢?该如何用呢?相信大家有一定的概率知识基础的同学,对先验概率及条件概率都比较清楚,在这里简单的说一下这条公式的含义:

1、首先P(A_{i}|B)代表的意思就是在B条件发生的情况下,Ai发生的概率

2、\sum{}_{j}^{}P(B|A_{j})P(A_{j})这公式代表的意思就是,在所有A类的结果中,B发生的概率之和,也就是B的概率

3、所以在这个公式,表达的意思就是(B条件发生的情况下,Ai发生的概率=Ai与B同时发生的概率占整个B发生的概率的多少)

 于是这个公式就可以转化为P(A_{i}|B) = \frac{P(B|A_{i})P(A_{i})}{P(B)} = \frac{P(A_{i}B)}{P(B)},(加上后面这个公式,是为了让我们更好的理解,实际上的使用,一般只用第一个等式的公式)

4、因此,由于P(B)是固定得,所以P(A_{i}|B)发生得概率与分子P(B|A_{i})P(A_{i})相关,也就是分子越大,概率越高

5、推导过程如下:

P(A_{i}|B) = \frac{P(A_{i}B) }{P(B)} = \frac{P(A_{i}B) }{\sum_{j}^{}P(A_{j}B)} = \frac{P(B|A_{i})P(A_{i})}{\sum_{j}^{}P(B|A_{j})P(A_{j})}

仔细品了一下这个公式,也确实发现其很适合用在分类的算法中;对于这个贝叶斯公式来说,就像是求B属于Ai的概率,运用在分类中,就得先求出B属于每一类中的可能,找出最大的,这就是朴素贝叶斯的算法思路。

由此得到朴素贝叶斯分类算法流程:

  1. 利用贝叶斯公式,求出预测得结果属于每一种类别得可能
  2. 找出最大值

2 案例介绍

有房婚姻状况拖欠贷款
单身no
已婚no
单身no
已婚no
离婚yes
已婚no
离婚no
单身yes
已婚no
单身yes
已婚no
已婚no
已婚yes

基于上面的数据,希望预测没房、单身的人,会不会拖欠贷款??? 

代码流程:

  1. 先将样本数据处理成字典,并统计每一类发生得个数(第一个函数)
  2. 将待预测得样本写入,并根据之前得样本数据,对分类结果进行求概率,得出最大概率得分类结果

代码如下: 

import pandas as pd


def handle_data(data):
    """
    将数据处理成字典,用于保存样本数据中的类别数据存储情况
    :param data: dataframe 数据源
    :return:样本数据中的类别数据字典,分类结果字典
    """
    # 初始化类别数据字典
    cate_dict = {}
    # 数据集表头列表(各个条件及分类结果)
    header_list = data.columns.tolist()
    # 条件列表
    factor_list = header_list[:-1]
    # 分类结果所在位置
    k = len(header_list) - 1

    # result_dict 为分类的结果类型字典
    result_dict = dict(data.iloc[:, k].value_counts())
    # 或使用如下语句:
    # result_dict = dict(data.iloc[:, -1].value_counts())
    result_dict_key = result_dict.keys()

    # 将每个分类结果写入 cate_dict
    # 循环各个分类结果
    for result_key in result_dict_key:
        # 如果类别数据字典不存在该分类结果,默认设置空字典
        if result_key not in cate_dict:
            # dict.setdefault(key, default=None)  键不存在于字典中,将会添加键并将值设为默认值
            cate_dict.setdefault(result_key, {})
        # 在该分类结果下,循环各个条件(因素)
        for factor in factor_list:
            # 如果该分类结果字典不存在该条件(因素),默认设置空字典
            if factor not in cate_dict[result_key]:
                cate_dict[result_key].setdefault(factor, {})
            # 获取该条件的分类列表
            factor_key_list = data[factor].value_counts().index.tolist()
            # 循环获取该条件的各个分类数量
            for key in factor_key_list:
                # 获取该分类结果下,该因素中某个分类的数量
                number = data[(data[header_list[k]] == result_key) & (data[factor] == key)].shape[0]
                if key not in cate_dict[result_key][factor]:
                    cate_dict[result_key][factor].setdefault(key, number)
    return cate_dict, result_dict


def calculate(cate_dict, result_dict, new_data):
    """
    对每个待预测得结果进行贝叶斯公式得计算,并得出预测类别与概率
    :param cate_dict: 样本数据中的类别数据字典
    :param result_dict: 分类结果字典
    :param new_data: 待预测的数据集
    :return: 预测结果列表
    """
    # 获取数据集的各个条件(因素)列表
    factor_list = new_data.columns.tolist()
    # 初始化预测结果列表
    result_list = []
    # 分类结果列表
    result_key_list = cate_dict.keys()

    # 循环预测新数据
    for i in range(len(new_data)):
        new_result_dict = {}
        # 循环计算各个分类指标的概率
        for result_key in result_key_list:
            # 该分类结果在所有分类结果中的占比
            all_ratio = result_dict[result_key] / sum(list(result_dict.values()))

            # 循环获取该分类结果下,该因素中各个 分类 在 该分类结果 中的占比
            for factor in factor_list:
                ratio = cate_dict[result_key][factor][new_data.iloc[i, factor_list.index(factor)]] / result_dict[result_key]
                # 总占比 乘以 该因素下的各个分类占比
                all_ratio *= ratio
            new_result_dict.setdefault(result_key, all_ratio)

        print(new_result_dict)
        # 获取占比最大的分类结果
        max_result_key = max(new_result_dict, key=new_result_dict.get)
        # 获取占比最大的分类结果的占比
        max_value = new_result_dict[max_result_key]

        result_list.append([max_result_key, max_value])
    return result_list


if __name__ == '__main__':
    file_path = "./朴素贝叶斯数据集.xlsx"
    data = pd.read_excel(file_path)
    print("数据源\n", data)
    # 待预测数据
    new_data = pd.DataFrame({"有房": "是", "婚姻状况": "已婚"}, index=[0])
    cate_dict, result_dict = handle_data(data)
    print(cate_dict)
    print(result_dict)
    result = calculate(cate_dict, result_dict, new_data)
    print(result)

运行结果:

数据源
    有房 婚姻状况 拖欠贷款
0   是   单身   no
1   否   已婚   no
2   否   单身   no
3   是   已婚   no
4   否   离婚  yes
5   否   已婚   no
6   是   离婚   no
7   否   单身  yes
8   否   已婚   no
9   否   单身  yes
10  是   已婚   no
11  是   已婚   no
12  是   已婚  yes
{'no': {'有房': {'否': 4, '是': 5}, '婚姻状况': {'已婚': 6, '单身': 2, '离婚': 1}}, 'yes': {'有房': {'否': 3, '是': 1}, '婚姻状况': {'已婚': 1, '单身': 2, '离婚': 1}}}
{'no': 9, 'yes': 4}
{'no': 0.2564102564102564, 'yes': 0.019230769230769232}
[['no', 0.2564102564102564]]

所以得出结果为NO!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐