学习了支持向量机算法后(SVM)想自己用一些数据集来尝试一下,在网络上找了一个垃圾邮件处理的数据集正好适用于 SVM 支持向量算法,所以在这里不讲 SVM 算法内容,而是分享我是如何用 Python 来实现的。具体数据集:[邮件数据集][1]
[1]: https://pan.baidu.com/s/1ivQmo-04fCa3qVG0SAiUtQ
1. 首先查看数据集
邮件中有很多中特征值,有些是有用的有些却无法帮助我们判断是否是垃圾邮件,所以这里我们选取其中 4 个特征值(From,To,Data,Content)。(1、邮件的内容分析——主要包含:发件人、收件人、发件时间以及邮件的内容)![分类][3](2、是否为垃圾邮件的标签,spam——是垃圾邮件;ham——不是垃圾邮件)
2. 分析数据集
分析:垃圾邮件处理问题是一个 ** 监督学习 **,仅用来分类邮件好坏固为 ** 二分类 ** 问题,分类内容为 ** 长文本 **。所有非向量形式的特征工程,首先要转换成向量的形式
该数据集适合很多种模型(贝叶斯,KNN),在这里我用 SVM 来解决
3. 开始进行数据清洗的工作
引入模块常用功能
import os
索引文件 (分类标签) 读取,该文件中分为两列
第一列:分类标签是否为垃圾邮件(是:spam、否:ham);
第二列:存放邮件对应文件夹路径,两列之间通过空格分割
def read_index_file(file_path):
type_dict={"spam":"1","ham":"0"} #用字典存放分类的垃圾邮件(0/1)index_file=open(file_path)
index_dict={} #存储分类后得到的结构字典
try:
for line in index_file: #按照行来读取
arr=line.split(" ") #从每行的第一个空格分开
if len(arr) == 2:
key,value = arr #将 key 和 value 分别赋值前后两段
#将 value 中的数据规整,将../data 去掉
value=value.replace("../data","").replace("\n","")
#将每一个值是 1 / 0 对应加入数据字典中
index_dict[value]=type_dict[key.lower()] #将 spam/ham 中可能出现的大写字母换成小写
finally:
index_file.close()
return index_dict
邮件的文件内容数据读取
def read_file(file_path):
#打开邮件只读 r, 编码方式 gb2312, 错误忽略
file=open(file_path,"r",encoding="gb2312",errors="ignore")
content_dict={}
try:
is_content = False
for line in file: #按行读取
line = line.strip() #去掉每行的空格
if line.startswith("From:"):
#将 From:后的内容加入数据字典, 将 key="from:",value=line[5:]5 以后的内容
content_dict["from:"]=line[5:]
elif line.startswith("To:"):
content_dict["to:"]=line[3:]
elif line.startswith("Date:"):
content_dict["date:"]=line[5:]
elif not line:
# 邮件内容与上面信息存在着第一个空行,遇到空行时,这里标记为 True 以便进行下面的邮件内容处理
# line 文件的行为空时是 False,不为空时是 True
is_content = True
#如果这一行是空行表明下一行开始是具体的内容(content), 则开始处理邮件
if is_content:
if "content" in content_dict:
#如果这一行有内容则加入字典
content_dict["content"] += line
else:
#如果这一行没有内容则不加入,继续下一行
content_dict["content"] = line
finally:
file.close()
return content_dict
3、邮件数据处理(内容的拼接, 并用逗号进行分割)
def process_file(file_path):
content_dict = read_file(file_path)
#进行处理 (拼接),get() 函数返回指定键的值,指定键的值不存在用指定的默认值 unkown 代替
#2,是将需要是数据特征取出来放在键值对中保存,3,则是将所有的特征值合并成,进行拼接
result_str = content_dict.get("from","unkown").replace(",","").strip()+","result_str += content_dict.get("to:","unkown").replace(",","").strip()+","
result_str += content_dict.get("date:","unkown").replace(",","").strip()+","result_str += content_dict.get("content","unkown").replace(",","").strip()
return result_str
4、开始进行数据处理——函数调用
## os.listdir 返回指定的文件夹包含的文件或文件夹包含的名称列表
index_dict = read_index_file('../data/full/index') #调用第一个函数
list0 = os.listdir('../data/data')
#开始打印 list0
for l1 in list0: #循环 000-299
l1_path = '../data/data/' + l1 #l1_path ../data/data/(215)
print('开始处理文件夹:'+l1_path)
list1 = os.listdir(l1_path) #list1:['000', '001', '002', '003'....'299']
#将 list1 打印出来
write_file_path = '../data/process01_' + l1 #用这个来存放
with open(write_file_path,"w",encoding='utf-8') as writer:
for l2 in list1: #l2 循环 000-299
l2_path = l1_path + "/" + l2 #l2_path = ../data/data/(000-215)/(000-299)
#这就得到了具体的文件内容,然后进行文件数据的读取
index_key = "/" + l1 + "/" + l2 #index_key = /(000-215)/(000-299)
if index_key in index_dict:
#读取数据
content_str = process_file(l2_path) #3 的函数,用来拼接数据元素,及特征值的合并
#添加分类标签(0/1),可用逗号隔开
content_str += "," + index_dict[index_key] + "\n"
#进行数据的输出
writer.writelines(content_str)
将所有构建好的内容进行合并
with open('../data/result_process01','w',encoding="utf-8") as writer:
#将所有内容合并到 result_process01 中
for l1 in list0:
file_path = '../data/process01_' + l1
print("开始合并文件:" + file_path)
with open(file_path, encoding='utf-8') as file:
for line in file:
writer.writelines(line)
清洗结果
到这一步数据的清洗就完成了,所有我们需要的特征值都被整合到了一起,并且为邮件做了加上了是否为垃圾邮件的标记。
推荐使用 jupyter, 注意文件的路径问题,特征工程和后面的分类会在下一篇。