一、实验内容简介
该实验主要使用频繁模式和关联规则进行数据挖掘,在已经使用过Apriori算法挖掘频繁模式后,这次使用FP-tree算法来编写和设计程序,依然使用不同规模的数据集来检验效果,最后分析和探讨实验结果,看其是否达到了理想的效果。本实验依然使用Python语言编写。
二、算法说明
首先简单介绍频繁模式和关联规则:
-
频繁模式一般是指频繁地出现在数据集中的模式。
-
关联规则是形如X→Y的蕴涵表达式,其中X和Y是不相交的项集,即X∩Y=∅。关联规则的强度可以用它的支持度(support)和置信度(confidence)来度量。计算公式如下:
-
支持度:support(A=>B)=P(A∪B),表示A和B同时出现的概率。
-
置信度:confidence(A=>B)=support(A∪B)/support(A),表示A和B同时出现的概率占A出现概率的比值。
-
强关联规则是指达到了最小支持度和最小置信度的关联规则。
然后再介绍FP-Tree算法:
2000年,Han Jiawei等人提出了基于频繁模式树(Frequent Pattern Tree, FP—Tree)的发现频繁模式的算法FP-Growth。其思想是构造一棵FP-Tree,把数据集中的数据映射到树上,再根据这棵FP-Tree找出所有频繁项集。
FP-Growth算法是指,通过两次扫描事务数据集,把每个事务所包含的频繁项目按其支持度降序压缩存储到FP-Tree中。在以后发现频繁模式的过程中,不需要再扫描事务数据集,而仅在FP-Tree中进行查找即可。通过递归调用FP-Growth的方法可直接产生频繁模式,因此在整个发现过程中也不需产生候选模式。由于只对数据集扫描两次,因此FP-Growth算法克服了Apriori算法中存在的问题,在执行效率上也明显好于Apriori算法。
上图为FP-Tree示意图,展示了该数据结构的构成方式。
三、算法分析与设计
了解完算法的基本原理后,现在开始真正实现该算法。首先需要读取最小支持度,读取数据集。这里的数据集可大可小,我用Python中的字典来表示数据
这里的数据存储格式与之前写Apriori算法时一样,使用字典来存储。然后由用户来输入支持度和置信度(因为这次还要挖掘关联规则,所以增加了置信度输入)。
作为FP-Tree的基础,首先构建树节点。一个节点有四个基本属性,分别节点名称、出现次数、双亲节点和孩子节点。因为这里不是二叉树,树的孩子节点个数不确定,因此用字典来存储,大小可控。
class Node:
def __init__(self, value, parent, count=0):
self.value = value
self.parent = parent
self.count = count
self.children = {
}
def addChild(self, child):
self.children.update(child)
def __init__(self, value, parent, count=0):
前置准备完成后,开始实现FP-Tree算法。FP-Tree算法可大致分为构建项头表、构建FP-Tree、利用条件模式基挖掘频繁模式和关联规则几步。把这几步集成到一个类中,这样避免了大量函数传参操作,思路更清晰。
首先构建项头表,先扫描一遍数据集挖掘频繁1项集,挖掘出来的数据按支持度降序排列,并按此顺序重新排列原数据集的数据,对于不符合要求的数据直接删除。
def first_scan(self):
"""
生成项头表,整理数据
"""
Dict = dict()
for i in self.data.values():
for j in i:
if j not in Dict.keys():
Dict.update({
j: 1})
else:
Dict[j] += 1
self.first_list = list(Dict.items())
self.first_list.sort(key=lambda l: l[1], reverse=True)
for i in range(len(self.first_list) - 1, 0, -1):
if self.first_list[i][1] < self.support * len(self.data):
continue
else:
rubbish = [self.first_list[j][0] for j in range(i + 1, len(self.first_list))]
self.first_list = self.first_list[:i + 1]
break
# 将原来的数据重新按支持度排序并剔除非频繁1项集
sort_refer = [i[0] for i in self.first_list]
for i in self.data.values():
for j in i:
if j in rubbish:
i.remove(j)
i.sort(key=lambda l: sort_refer.index(l))
# 添加频繁1项集
self.pinfan.extend([list(i) for i in self.first_list])
# 整理项头表
self.value_list = [i[0] for i in self.first_list]
temp = {
}
for i in self.first_list:
temp.update({
i[0]: [