数据挖掘实验2python编写贝叶斯分类器

1. Introduction

本文基于前文说的朴素贝叶斯原理,参考圣地亚哥州立大学的实验编写了一个简单的朴素贝叶斯分类器,并利用测试数据进行了测试。

项目地址:

2. 分类器编写

2.1数据说明

采用“adult”数据集,输入文件是adult.data,测试文件是adult.test。数据中一行为一个条目,表示一个人

数据集中的变量

变量名 意义
age 年龄
type_employer 职业类型,个体,政府等等
fnlwgt 该变量将被我们忽略
education 学历
education_num 学历的数字等级(和上一个一样,将被忽略)
marital 婚姻状况
occupation 职业
relationship 不清楚如何表述
race 人种
sex 生理性别
capital_gain 资本收益
capital_loss 资本支出
hr_per_week 每周工作时长
country 国籍
income 年收入是否>50k

由于参考文章中使用的是R语言进行处理,R语言在数据挖掘和统计上优势极大,几乎就是为其而生。python中也有numpy库,但是这里仅用了numpy库的median取中位数功能,其他还是以python原生类型来处理。

2.2 框架规划

参考使用Python编写朴素贝叶斯分类器,我们也主要使用字典来实现统计,但是可以分成两个字典,一个是>50k的dataset_high,一个是<=50k的dataset_low。

class DataSet:
    def __init__(self):
        # 存储读入的原始数据
        self.data = []
        # 支出的中位数
        self.loss_mid = 0
        # 收入的中位数
        self.gain_mid = 0
        # 工作时长的中位数
        self.hours_mid = 0
        # 年龄的中位数
        self.age_mid = 0
        # 统计的数据,主要部分
        self.classfied_dataset = None
        # 总数据条目
        self.len_data = 0

最后的计算是前文说的:

最后两者取大者,则就是所建模型的判定,是否大于50k。

公式化简:

由于P(输入数据的特征)对于一条数据,两个公式来说,是相同的,所以略去计算。

2.3 输入数据预处理

a = ["age", "type_employer", "fnlwgt", "education", "education_num", "marital", "occupation", "relationship", "race",
     "sex", "capital_gain", "capital_loss", "hr_per_week", "country", "income"]
classfiled_data = {}
loss_median = loss
gain_median = gain
for node in a:
    classfiled_data[node] = {}
    for line in data:
        if len(line) < 10:
            continue
            for node in a:
                if line[a.index(node)] in classfiled_data[node]:
                    classfiled_data[node][line[a.index(node)]] += 1
                    else:
                        classfiled_data[node][line[a.index(node)]] = 1

列表a就是所有的字段,将所有的数据都按照对应字段,统计到classfiled_data上去,最后形成的形式如下:

# 打印classfiled_data的输出,这是已经简化过的输出
{education:
{'Prof-school': 153, 'dropout': 4009, 'Doctorate': 107, 'HS-grad': 8826, 'Bachelors': 3134, 'Assoc': 1823, 'Some-college': 5904, 'Masters': 764},
marital:
{'Widowed': 908, 'Never-married': 10192, 'not-married': 5323, 'Married': 8297},
country:
{'other': 133, 'United-States': 21999, 'British-Commonwealth': 230, 'SE-Asia': 242, 'Euro_1': 159, 'Euro_2': 122, '?': 437, 'South': 64, 'China': 100, 'Latin-America': 1027, 'South-America': 207},
income:
{'<=50K': 24720},
capital_gain:
{'low': 0, 'none': 23685, 'high': 1035},
relationship:
{'Not-in-family': 7449, 'Own-child': 5001, 'Other-relative': 944, 'Husband': 7275, 'Wife': 823, 'Unmarried': 3228},}

即classfiled_data的第一层字段是a里面的字段,每个字段又对应不同类型的子字段,数字是统计所有数据的出现次数。

2.4 字段简化

舍弃没用的fnlwgt和重复的education_num字段

对于职业类型字段,Never-worked和without-Pay可以合并为Not-working字段,类似的,也可以把其他一些字段进行合并,合并的步骤是先在classfiled_data['type_employer']里新建一个'not-working'的key,然后其value就是原来['Never-worked', 'Without-pay']的数值之和。在写了很长的代码以后,我将其提取出来做成了一个函数:

def tiny(a_list, category, new_name):
    if new_name not in classfiled_data[category]:
        classfiled_data[category][new_name] = 0
        for key in list(classfiled_data[category]):
            if key in a_list and key != new_name:
                classfiled_data[category][new_name] += classfiled_data[category][key]
                del classfiled_data[category][key]

tiny(['Never-worked', 'Without-pay'], 'type_employer', 'not-working')
tiny(['Local-gov', 'State-gov'], 'type_employer', 'other-govt')
tiny(['Self-emp-inc', 'Self-emp-not-inc'], 'type_employer', 'self-employed')

同样对其他字段也进行了类似的化简。

这里有这样几个字段需要单独处理:

  • capital_gain 利用中位数划分成三部分:(-INF, 0] (0, mid] (mid, INF]
  • capital_loss 同上
  • hr_per_week 工作时间按照10小时间隔划分了。最大值99,映射到100s上
  • age 按照5为间隔划分了20组,

3. 测试数据

由于前面对数据进行了化简,所以测试数据的输入也需要按照上面的划分进行映射,我代码里直接使用生成好的字典进行映射。

针对每条数据,计算P(输入数据的特征|>50k) 和P(输入数据的特征|<=50k),取大的返回结果。

最后测试结果如下:

模型的判断正确的次数:	13206
错误的次数	3075
正确率:	0.811130
路漫漫其修远兮,吾将上下而求索

文章若未注明转载皆为原创,如需转载请注明出处FindHao博客及文章链接,文章markdown格式源码现已开放,欢迎转载。文章源码地址:

分享到:

You may like..(由于采用了谷歌的推荐系统,需要对本站关闭广告过滤~)

Find

新浪微博(FindSpace博客)QQ群:不安分的Coder(375670127) 不安分的Coder

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*