数据挖掘-朴素贝叶斯分类器demo

应一位同学要求,帮忙写个基于他们书本上的例子的朴素贝叶斯分类器,然后就有此成果了。不过内容也是相当朴素,看看就好。


这里的朴素贝叶斯如下:
P(H|X) = P(X|H)*P(H) / P(X)

而且这里针对的也只是分类属性(相当朴素)


数据变换规则如下:
age : youth==1, middle_aged=2, senior==3
income : low==1, medium==2, high==3
student : no==0, yes==1
credit_ratin : fair==0, excellent==1
buys_computer : no==0, yes==1
训练数据如下:
age income  student credit_rating buys_computer
1   3   0   0   0
1   3   0   1   0
2   3   0   0   1
3   2   0   0   1
3   1   1   0   1
3   1   1   1   0
2   1   1   1   1
1   2   0   0   0
1   1   1   0   1
3   2   1   0   1
1   2   1   1   1
2   2   0   1   1
2   3   1   0   1
3   2   0   1   0
代码如下:
import re


def load_data(path):
    '''
    读取文件,载入训练数据
    :param path: 训练数据路径
    :return: 训练数据
             labels是 数据属性,例如:age,income等,
             training_data是 具体数据,格式像:[{'age':1,'income':1},{'age':2,'income':2}], 没有列出所有属性
    '''
    training_data = list()
    with open(path, encoding='utf-8') as file:
        data = file.read().split('\n')
        labels = re.split(r'[\s]+', data[0])
        for content in data[1:]:
            parts = re.split(r'[\s]+', content.strip())
            item = dict()
            for index, part in enumerate(parts):
                item[labels[index]] = part
            training_data.append(item)
    return labels, training_data


class Classifier:
    '''
    依据朴素贝叶斯分类公式P(H|X) = P(X|H)*P(H) / P(X)
    做的一个分类器
    '''

    def __init__(self, classify_tag, decision=3, train_data_path='training_data.txt'):
        '''
        初始化分类器
        :param decision: 计算过程中保留的小数位,应用场景:round(小数,保留的小数位个数)
        :param train_data_path: 训练数据的路径
        '''

        self.labels, self.training_data = load_data(train_data_path)
        self.labels.remove(classify_tag)
        self.load_all_clazzs(classify_tag)
        self.decision = decision

    def load_all_clazzs(self, tag):
        '''
        读取所有的分类结果
        :param tag: 用于分类的属性,这里是 buys_computer
        :return: 所有的分类结果,例如:['0','1']
        '''
        self.clazz_tag = tag
        self.clazz_values = set()
        for item in self.training_data:
            self.clazz_values.add(item[tag])
        self.clazz_values = list(self.clazz_values)

    def predict(self, item):
        '''
        预测输入数据的分类结果
        :param item: 待预测对象
        :param classify_tag: 分类属性,这里是'buys_computer'
        :return: 分类结果, 例如:'1'
        '''

        PXs = list()
        Ps = list()

        # 统计每种类别对应的P(Ci),P(X|Ci)
        for value in self.clazz_values:
            rules = {self.clazz_tag: value}

            p = self.get_P_on_base(rules)
            Ps.append(p)

            px = self.get_PX_on_base(item, rules)
            PXs.append(px)

        print(self.clazz_tag + ' :', self.clazz_values)
        print('P(Ci) : ', Ps)
        print('P(X|Ci) : ', PXs)

        # 遍历所有的值,从而找到最大化的 P(Ci) * P(X|Ci)
        target = [0, 0]  # 格式为[位置下标,P(Ci) * P(X|Ci)的值]
        for index, p in enumerate(Ps):
            px = PXs[index]
            p_px = round(p * px, self.decision)
            if p_px > target[1]:
                target[0] = index
                target[1] = p_px

        print('Max P(Ci)P(X|Ci) : ', target[1])

        return self.clazz_values[target[0]]

    def count(self, rules):
        '''
        计算在训练数据中,所有符合条件的元组个数
        :param rules: 所有条件,数据结构为元组,格式例如:{'age':'1','buys_computer':'1'}
        :return: 符合的个数
        '''

        # 通过遍历训练数据,再遍历条件,判断是否当前数据符合条件,符合(没有执行break)则进入else,计数+1
        hit_num = 0
        for item in self.training_data:
            for key, value in rules.items():
                if item[key] != value:
                    break
            else:
                hit_num += 1
        return hit_num

    def get_P_on_base(self, rules):
        '''
        计算先验概率,P(Ci)
        :param rules:类别要求,格式例如:{'buys_computer':'1'}
        :return:P(Ci)的值
        '''
        hit_num = self.count(rules)
        P = round(hit_num / len(self.training_data), self.decision)
        return P

    def get_PX_on_base(self, item, rules):
        '''
        获取P(X|H)
        :param item: 相当于X,格式例如:{'age':1,'income':1} ,此处没有列出所有属性
        :param rules: 类别的要求,格式例如:{'buys_computer':'1'}
        :return: P(X|H)的值
        '''

        base_num = self.count(rules)
        PX_on_base = 1  # 后面是做P(X|Ci)的累乘,故初始化为 1
        for label in self.labels:
            # 获取所有的统计要求,例如:{'buys_computer':'1','age':'1'}
            tmp_rule = {label: item[label]}
            tmp_rule.update(rules)

            hit_num = self.count(tmp_rule)

            # 计算P(X|Ci)并且累乘进PX_on_base
            PX_on_base = round(PX_on_base * hit_num / base_num, self.decision)

        return PX_on_base


if __name__ == '__main__':
    # 数据变换
    # age : youth==1, middle_aged=2, senior==3
    # income : low==1, medium==2, high==3
    # student : no==0, yes==1
    # credit_ratin : fair==0, excellent==1
    # buys_computer : no==0, yes==1

    # 待判断数据
    item = {'age': '1', 'income': '2', 'student': '1', 'credit_rating': '0'}

    # 分类的标签
    clazz_tag = 'buys_computer'

    classifier = Classifier(clazz_tag)

    # 预测分类结果
    ans = classifier.predict(item)

    print('\nresult:')
    print(clazz_tag + ':' + ans)

是不是有很多注释?完全不是我的风格啊,可是没办法,你懂得。

后续:

代码简单如此,拿来用是不可能的了。其中,据我粗略的了解了一下,还可以针对连续值属性的求概率做补充,对处理零概率事件使用拉普拉斯校准,等等。而且对于代码效率等问题上,完全没做啥子。有兴趣或许后面会改改吧。


啊啊,对于文中所有的概念,仅限于和我有一致共识前提的人能看懂,我不解释(毕竟我也不是很懂)。

猜你喜欢

转载自blog.csdn.net/zyssky465/article/details/79956330