数据挖掘(二) Apriori算法的python实现
使用的数据承接之前处理好的情报词库
数据挖掘(一) TF-IDF算法
Apriori算法的目的是为了查找,一些主题词元素的出现,是否存在关联性,算法流程如下:
Apriori算法
关联规则提取(判断)与存储
结合注释代码:
包括频繁模式的查找
class Apriori算法:
def __init__(self, 数据库):
"""
求取频繁模式
:param 数据库: 输入的是一个二维列表,存储着一条一条的主题词或者关键字,
都是根据某一语义在其下共现的事务
"""
self.数据库 = 数据库
self.数据库长度 = len(数据库)
self.k = 1
self.事务数据最长长度 = 0
self.频繁模式总表 = []
self.当前频繁模式 = []
self.历史频繁模式 = []
self.频繁阈值 = 6 / len(数据库)
"""
原处于各个位置的词汇都可以成为待选模式的一员
"""
待选1模式 = {}
for 词汇条 in self.数据库:
if len(词汇条) > self.事务数据最长长度:
self.事务数据最长长度 = len(词汇条)
for 词 in 词汇条:
try:
待选1模式[词] += 1
except:
待选1模式.update({词: 1})
"""
根据出现的频次,按照阈值划分,去除待选模式的不频繁元素
"""
频繁1模式列表 = {}
for 频繁词 in 待选1模式:
if 待选1模式[频繁词] / self.数据库长度 > self.频繁阈值:
频繁1模式列表.update({频繁词: 待选1模式[频繁词] / self.数据库长度})
self.频繁模式总表.append([])
self.频繁模式总表[-1].append(list(频繁1模式列表.keys()))
self.频繁模式总表[-1].append(频繁1模式列表)
def 待选模式生成(self):
"""
从频繁1模式中抽取 k个元素,构成新的集合,是原集合的 k元选组合
:return:
"""
if self.k < self.事务数据最长长度:
temp = list(itertools.combinations(self.频繁模式总表[0][0], self.k))
self.k += 1
return temp
else:
return None
@staticmethod
def 查询函数(输入列表, 新元素, 查询参数):
"""
函数功能是解决一个问题,在将一个元素(可以是一个字符串,也可以是一个列表)
添加入新的列表时,先查询在这个列表是否存在了这个元素
:param 输入列表: 待被添加进新元素的原列表
:param 新元素: 待添加元素
:param 查询参数: 新元素的数据长度
:return: 布尔值:
True: 未存在(即可以添加的意思)
False: 已存在(就不添加了)
"""
index = 0
for 子元素 in 输入列表:
查询元素个数 = 0
for 查询元素个数 in range(查询参数):
try:
if 子元素[查询元素个数] == 新元素[查询元素个数]:
查询元素个数 += 1
except:
pass
if 查询元素个数 == 查询参数 and 查询元素个数 != 0:
index += 1
break
if index:
return False
else:
return True
@staticmethod
def 列表元素拼接(输入列表):
"""
由于k>=2的频繁模式,它的元素是k个字符串的集合,
在做字典计数以及存储时,需要拼接,这里进行一下函数封装
:param 输入列表: 待拼接列表
:return: 拼接结果,以 + 作为拼接标识符
"""
temp = ''
for i in 输入列表:
temp += i + '+'
return temp[:-2] # 末尾的 + 就不需要了
def 频繁模式生成(self):
self.当前频繁模式 = self.待选模式生成()
self.历史频繁模式 = self.频繁模式总表[-1][0]
"""
被注释掉的是一个优化过程,其子模式不是频繁模式
那么它也不是,就不需要参与接下来的支持度查找了
但是存在一定逻辑问题,当前并未加入这一段
"""
# temp = []
# for i in self.当前频繁模式:
# k = 0
# for j in self.历史频繁模式:
# if self.查询函数(i, j, len(j)):
# k += 1
# if k:
# temp.append(i)
# self.当前频繁模式 = temp
当前表 = {}
for 频繁模式打分 in tqdm(self.当前频繁模式):
for 词汇条 in self.数据库:
if len(频繁模式打分) < len(词汇条):
pass
else:
if self.查询函数(频繁模式打分, 词汇条, len(词汇条)):
pass
else:
try:
当前表[self.列表元素拼接(频繁模式打分)] += 1
except:
当前表.update({self.列表元素拼接(频繁模式打分): 1})
频繁k模式列表 = {}
for 频繁词 in 当前表:
if 当前表[频繁词] > 0:
频繁k模式列表.update({频繁词: 当前表[频繁词] / self.数据库长度})
self.频繁模式总表.append([])
self.频繁模式总表[-1].append(list(频繁k模式列表.keys()))
self.频繁模式总表[-1].append(频繁k模式列表)
pass
def start(self):
# while self.k < 5:
self.频繁模式生成()
def 关联规则提取与储存(self):
strings = ''
for i in self.频繁模式总表[1][0]:
for j in self.频繁模式总表[0][0]:
temp = i.split('+')
if temp[0] == j or temp[1] == j:
置信度 = self.频繁模式总表[1][1][i] / self.频繁模式总表[0][1][j]
if 置信度 > 0.24:
print('<Class/主题词/{}> <Class/Relation/同一情报下> <Class/主题词/{}>'.format(temp[0], temp[1]))
strings += '<Class/topic/{}> <Class/Relation/one Description> <Class/topic/{}>\n'.format(
temp[0], temp[1])
with open('out.xml', 'w') as f:
f.write(strings)
f.close()
结合上一次全部的数据挖掘过程
import csv
from math import log
from tqdm import tqdm
import itertools
# 读取数据
with open('attacks.csv', 'r', encoding='UTF-8') as csv文件:
文件内容 = csv.reader(csv文件)
文件内容列表 = list(文件内容)
# print(文件内容列表)
pass
# 列表存储词条
大词条库 = []
时间国家库 = []
时间城市库 = []
情报词库 = []
def 删除字符函数(输入0):
"""
:param 输入0: 待处理的字符串
:return: 删除无用字符后的字符串
"""
删除字符 = "()[],.'&-…/1234567890:"
处理 = 输入0
for i in 删除字符:
处理 = 处理.replace(i, ' ')
# 由于 " 无法和 "" 同时出现,这里再进行一次删除后返回
return 处理.replace('"', ' ')
for 序号0, 记录 in enumerate(文件内容列表):
"""
记录[6]:即Description下的语句字符串
删除字符函数(记录[6]):返回删除了无关字符的字符串
删除字符函数(记录[6]).split(' '):以 " " 空格进行字符串划分,
返回一个分割成字符的列表
然后添加进大词条库
"""
大词条库.append(
删除字符函数(记录[6]).split(' '))
"""
当取出语句字符串后,将它删除,以便之后存入选好的主题词列表
判断语句表示表头Description不做改动
"""
if 序号0 != 0:
文件内容列表[序号0].remove(记录[6])
class 我的tf_idf类:
def __init__(self, 待处理大词条库, TFIDF阈值=0.3, 频繁模式阈值=0.2):
"""
:param 待处理大词条库: 2维列表形式,第一维列表保存有一个段字库
"""
self.初始大词条库 = 待处理大词条库
self.语句总数 = len(待处理大词条库)
self.总词条词频库 = []
self.总词条单词评分库 = []
self.词在句出现总频库 = {}
self.TFIDF阈值 = TFIDF阈值
self.总词条主题词库 = []
self.频繁模式阈值 = 频繁模式阈值
self.频繁模式表 = []
def 语句词库(self, 词条分词库):
"""
:param 词条分词库: 输入的是一条语句的词汇列表
:return: 返回该词汇列表各个词汇的词频统计字典
以及self.词在句出现总频库 这个是统计词汇在各个语句中出现的情况
"""
"""
词条词频库:使用字典进行词频统计,字典使用字符串(称为键)进行索引,得到键值,
键值反复被键(也就是某个词汇)索引累加,完成词频统计
重复记录:在完成语句词频的统计时,也完成了对这个词在语句中出现的次数,
由于一句只统计一次,所以需要在此记录一下,是否这个词在该列表被统计过了
"""
词条词频库 = {}
重复记录 = []
for 单词 in 词条分词库:
"""
这里出现的try/except都是为了做一件事,
try:给字典里面的字符串键值加1,
except:这个键不在字典里,则添加进去,初始化键值为1
"""
try:
词条词频库[单词] += 1
"""
若单词在重复记录出现了,则跳过
若没有,则记录入重复记录列表
同时在self.词在句出现总频库中给这个词汇的键值加1
"""
if 单词 in 重复记录:
pass
else:
重复记录.append(单词)
try:
self.词在句出现总频库[单词] += 1
except:
self.词在句出现总频库.update({单词: 1})
pass
except:
词条词频库.update({单词: 1})
"""
若单词在重复记录出现了,则跳过
若没有,则记录入重复记录列表
同时在self.词在句出现总频库中给这个词汇的键值加1
"""
if 单词 in 重复记录:
pass
else:
重复记录.append(单词)
try:
self.词在句出现总频库[单词] += 1
except:
self.词在句出现总频库.update({单词: 1})
pass
return 词条词频库
pass
def 文本词库(self):
"""
:return: None
"""
"""
对每条语句词库重复调用
在self.总词条词频库记录所有的语句以及其词频
"""
for 词条 in self.初始大词条库:
self.总词条词频库.append(self.语句词库(词条))
pass
def tf_idf函数(self, 单词条频库):
"""
:param 单词条频库: 输入单词条评分
:return: 无
"""
"""
这里还使用了self.词在句出现总频库,
也就是查找词在某些句子中出现的语句条数
"""
单词评分库 = {}
词总数 = sum(list(单词条频库.values()))
for 单词 in 单词条频库:
单词TF分数 = 单词条频库[单词] / 词总数
单词IDF分数 = log(self.语句总数 / (self.词在句出现总频库[单词] + 1))
单词评分库.update({单词: 单词TF分数 * 单词IDF分数})
return 单词评分库
def tf_idf加速函数(self):
self.总词条单词评分库 = list(map(self.tf_idf函数, self.总词条词频库))
pass
def tf_idf阈值划分(self, 单词条评分):
"""
:param 单词条评分: 输入单词条评分库
:return:
"""
主题词 = {}
for 词汇 in 单词条评分:
if 单词条评分[词汇] >= self.TFIDF阈值:
主题词.update({词汇: 单词条评分[词汇]})
pass
pass
return 主题词
def tf_idf阈值划分加速函数(self):
"""
对 tf_idf阈值划分调用
:return: 无
"""
self.总词条主题词库 = list(map(self.tf_idf阈值划分, self.总词条单词评分库))
pass
def TFIDF(self):
"""
启动函数
:return: 无
"""
self.文本词库()
self.tf_idf加速函数()
self.tf_idf阈值划分加速函数()
class Apriori算法:
def __init__(self, 数据库):
"""
求取频繁模式
:param 数据库: 输入的是一个二维列表,存储着一条一条的主题词或者关键字,
都是根据某一语义在其下共现的事务
"""
self.数据库 = 数据库
self.数据库长度 = len(数据库)
self.k = 1
self.事务数据最长长度 = 0
self.频繁模式总表 = []
self.当前频繁模式 = []
self.历史频繁模式 = []
self.频繁阈值 = 6 / len(数据库)
"""
原处于各个位置的词汇都可以成为待选模式的一员
"""
待选1模式 = {}
for 词汇条 in self.数据库:
if len(词汇条) > self.事务数据最长长度:
self.事务数据最长长度 = len(词汇条)
for 词 in 词汇条:
try:
待选1模式[词] += 1
except:
待选1模式.update({词: 1})
"""
根据出现的频次,按照阈值划分,去除待选模式的不频繁元素
"""
频繁1模式列表 = {}
for 频繁词 in 待选1模式:
if 待选1模式[频繁词] / self.数据库长度 > self.频繁阈值:
频繁1模式列表.update({频繁词: 待选1模式[频繁词] / self.数据库长度})
self.频繁模式总表.append([])
self.频繁模式总表[-1].append(list(频繁1模式列表.keys()))
self.频繁模式总表[-1].append(频繁1模式列表)
def 待选模式生成(self):
"""
从频繁1模式中抽取 k个元素,构成新的集合,是原集合的 k元选组合
:return:
"""
if self.k < self.事务数据最长长度:
temp = list(itertools.combinations(self.频繁模式总表[0][0], self.k))
self.k += 1
return temp
else:
return None
@staticmethod
def 查询函数(输入列表, 新元素, 查询参数):
"""
函数功能是解决一个问题,在将一个元素(可以是一个字符串,也可以是一个列表)
添加入新的列表时,先查询在这个列表是否存在了这个元素
:param 输入列表: 待被添加进新元素的原列表
:param 新元素: 待添加元素
:param 查询参数: 新元素的数据长度
:return: 布尔值:
True: 未存在(即可以添加的意思)
False: 已存在(就不添加了)
"""
index = 0
for 子元素 in 输入列表:
查询元素个数 = 0
for 查询元素个数 in range(查询参数):
try:
if 子元素[查询元素个数] == 新元素[查询元素个数]:
查询元素个数 += 1
except:
pass
if 查询元素个数 == 查询参数 and 查询元素个数 != 0:
index += 1
break
if index:
return False
else:
return True
@staticmethod
def 列表元素拼接(输入列表):
"""
由于k>=2的频繁模式,它的元素是k个字符串的集合,
在做字典计数以及存储时,需要拼接,这里进行一下函数封装
:param 输入列表: 待拼接列表
:return: 拼接结果,以 + 作为拼接标识符
"""
temp = ''
for i in 输入列表:
temp += i + '+'
return temp[:-2] # 末尾的 + 就不需要了
def 频繁模式生成(self):
self.当前频繁模式 = self.待选模式生成()
self.历史频繁模式 = self.频繁模式总表[-1][0]
"""
被注释掉的是一个优化过程,其子模式不是频繁模式
那么它也不是,就不需要参与接下来的支持度查找了
但是存在一定逻辑问题,当前并未加入这一段
"""
# temp = []
# for i in self.当前频繁模式:
# k = 0
# for j in self.历史频繁模式:
# if self.查询函数(i, j, len(j)):
# k += 1
# if k:
# temp.append(i)
# self.当前频繁模式 = temp
当前表 = {}
for 频繁模式打分 in tqdm(self.当前频繁模式):
for 词汇条 in self.数据库:
if len(频繁模式打分) < len(词汇条):
pass
else:
if self.查询函数(频繁模式打分, 词汇条, len(词汇条)):
pass
else:
try:
当前表[self.列表元素拼接(频繁模式打分)] += 1
except:
当前表.update({self.列表元素拼接(频繁模式打分): 1})
频繁k模式列表 = {}
for 频繁词 in 当前表:
if 当前表[频繁词] > 0:
频繁k模式列表.update({频繁词: 当前表[频繁词] / self.数据库长度})
self.频繁模式总表.append([])
self.频繁模式总表[-1].append(list(频繁k模式列表.keys()))
self.频繁模式总表[-1].append(频繁k模式列表)
pass
def start(self):
# while self.k < 5:
self.频繁模式生成()
def 关联规则提取与储存(self):
strings = ''
for i in self.频繁模式总表[1][0]:
for j in self.频繁模式总表[0][0]:
temp = i.split('+')
if temp[0] == j or temp[1] == j:
置信度 = self.频繁模式总表[1][1][i] / self.频繁模式总表[0][1][j]
if 置信度 > 0.1:
print('<Class/主题词/{}> <Class/Relation/同一情报下> <Class/主题词/{}>'.format(temp[0], temp[1]))
strings += '<Class/topic/{}> <Class/Relation/one Description> <Class/topic/{}>\n'.format(
temp[0], temp[1])
with open('out.xml', 'w') as f:
f.write(strings)
f.close()
####################################################################################################################
# 词的TF_IDF的评分计算
####################################################################################################################
工作啦 = 我的tf_idf类(大词条库, TFIDF阈值=0.35)
工作啦.TFIDF()
print(工作啦.总词条主题词库)
def 结果表格形式():
"""
这个函数是为了把原来删掉了Description的文件内容列表
填充进主题词列表,方便后面的主题词处理
同时把待选的时间国家库/时间城市库提取做好
:return:
"""
import pandas as pd
for 序号1, 主题词 in enumerate(工作啦.总词条主题词库):
if 序号1 != 0:
文件内容列表[序号1].append([])
if 主题词:
for 主题词0 in 主题词:
文件内容列表[序号1][-1].append(主题词0)
else:
文件内容列表[序号1][-1].append([])
主题词提取 = pd.DataFrame(文件内容列表[1:], columns=文件内容列表[0]).drop(['index'], axis=1)
print(主题词提取)
for 序号3, 记录1 in enumerate(文件内容列表):
# print(记录)
if 记录1[6]:
时间国家库.append([记录1[1], 记录1[2]])
时间城市库.append([记录1[1], 记录1[3]])
# print(主题词提取)
结果表格形式()
####################################################################################################################
# 等价语义划分
####################################################################################################################
def 查询函数(输入列表, 新元素, 查询参数):
"""
函数功能是解决一个问题,在将一个元素(可以是一个字符串,也可以是一个列表)
添加入新的列表时,先查询在这个列表是否存在了这个元素
:param 输入列表: 待被添加进新元素的原列表
:param 新元素: 待添加元素
:param 查询参数: 新元素的数据长度
:return: 布尔值:
True: 未存在(即可以添加的意思)
False: 已存在(就不添加了)
"""
index = 0
for 子元素 in 输入列表:
查询元素个数 = 0
for 查询元素个数 in range(查询参数):
try:
if 子元素[查询元素个数] == 新元素[查询元素个数]:
查询元素个数 += 1
except:
pass
if 查询元素个数 == 查询参数 and 查询元素个数 != 0:
index += 1
break
if index:
return False
else:
return True
####################################################################################################################
# 基于情报语义
def 情报语义事务():
"""
在从工作啦.总词条主题词库提取主题词至情报词库的过程中
同时,进行该词库是否在情报词库进行判断
:return: 无
"""
"""
tqdm库是可以显示处理进度的函数包
"""
for 词条主题词 in tqdm(工作啦.总词条主题词库):
if 词条主题词:
# if 查询函数(情报词库, list(词条主题词.keys()), len(词条主题词)):
# good = list(词条主题词.keys())
情报词库.append(list(词条主题词.keys()))
情报语义事务()
print(情报词库)
#########################################
# 基于时间国家语义
def 时间国家事务():
"""
在从时间国家库提取关键字组合(即时间和国家的组合)
至时间国家事务库0的过程中,
同时,进行该是否在时间国家事务库0进行判断
:return: 基于时间国家语义的时间国家事务库0
"""
时间国家事务库0 = []
for 序号2, 时间国家 in enumerate(时间国家库):
if 序号2:
if 查询函数(时间国家事务库0, 时间国家, 2):
时间国家事务库0.append(时间国家)
return 时间国家事务库0
时间国家事务库 = 时间国家事务()
print(时间国家事务库)
#########################################
# 基于时间城市语义
def 时间城市事务():
"""
在从时间城市库提取关键字组合(即时间和城市的组合)
至时间城市事务库0的过程中,
同时,进行该是否在时间城市事务库0进行判断
:return: 基于时间城市语义的时间城市事务0
"""
时间城市事务库0 = []
序号 = 0
for 时间城市 in tqdm(时间城市库):
if 序号:
if 查询函数(时间城市事务库0, 时间城市, 2):
时间城市事务库0.append(时间城市)
序号 += 1
return 时间城市事务库0
# 时间城市事务库 = 时间城市事务()
# print(时间城市事务库)
####################################################################################################################
# 频繁模式查找与规则输出
####################################################################################################################
工作1 = Apriori算法(情报词库[:1000])
工作1.待选模式生成()
工作1.start()
工作1.关联规则提取与储存()
print((工作1.频繁模式总表[0][0]))
print((工作1.频繁模式总表[1][0]))