第1关:构建FP-tree(不能粘贴复制,我只能放图了)
任务描述
本关任务:创建FP
树,并且更新FP
树,向其中插入新节点。
相关知识
事务ID
事务中的元素项:
001 r,z,h,j,p
002 z,y,x,w,v,u,t,s
003 z
004 r,x,n,o,s
005 y,r,x,z,q,t,p
006 y,z,x,e,q,s,t,m
这份数据一共有6
条记录,每条记录中的元素就是项,第1
条记录中有5
个项,分别为:r
,z
,h
,j
,p
。项的集合就是项集,比如,[r]
是一个项集,[r,z]
是一个项集,[r,z,h,j,p]
也是一个项集,项集是指项的任意组合。而频繁项集是指,那些在记录中经常一起出现的项组合成的集合。那么,“经常”是怎么衡量的呢?这里就涉及到支持度的概念。支持度是说出现的次数,它可以针对单个项,也可以针对项的组合,在这6
条数据记录中,r
一共出现了3
次,所以r
的支持度是3
,项集(r,x)
出现了2
次,所以(r,x)
的支持度是2
。
构建FP树的准备工作
FP
代表频繁模式(Frequent Pattern)
。我们先看看FP
树长什么样子。以下这棵FP
树是根据上面那份数据记录建立的。
可以看出,一棵FP
树看上去与计算机科学中的其他树结构类似,但是它包含着连接相似节点的链接(图中的红色虚线部分)。 相似节点是指前缀路径不同的项,如在上面的FP
树中r
的前缀路径有3
个,分别为(z)
,(z,x,y)
,(x,s)
,于是,这些不同的r
就叫做相似节点。用Python
构建FP
树时会创建一个字典结构存储这些相似元素。
构造树节点
在构建FP
树之前,我们要先定义一个类,用来保存树的每一个节点。
class treeNode:#构造树节点
def __init__(self, nameValue, numOccur, parentNode):
self.name = nameValue#节点名称
self.count = numOccur #节点出现的次数
self.nodeLink = None #链接指向的下一个节点
self.parent = parentNode#父节点
self.children = {}#子节点
def inc(self, numOccur):#该函数用来增加节点出现的次数
self.count += numOccur
def disp(self, ind=1):#展示节点名称和出现的次数
print(' ' * ind, self.name, ' ', self.count)
for child in self.children.values():
child.disp(ind + 1)
运行下面这段代码:
rootNode=treeNode('pyramid',9,None) #创建节点
rootNode.children['eye']=treeNode('eye',13,None) #增加子节点
rootNode.children['phoenix']=treeNode('phoenix',3,None) #增加另一个子节点
rootNode.disp() #展示树
运行结果:
由于“eye”
和“phoenix”
都是”pyramid“
的子节点,所以在展示树的结构时,“eye”
和“phoenix”
的缩进深度相同,都比”pyramid“
的缩进深度更深一级。
把原始事务数据集处理成字典的形式
除此之外,我们还需要把原始事务数据集处理成字典的形式,方面后面的函数调用。 定义两个函数,如下:
from collections import OrderedDict
def loadSimpDat():
simpDat=[['r','z','h','j','p'],
['z','y','x','w','v','u','t','s'],['z'], ['r','x','n','o','s'],['y','r','x','z','q','t','p'],['y','z','x','e','q','s','t','m']]
return simpDat
def createInitSet(dataSet):
retDict=OrderedDict()
for trans in dataSet:
retDict[frozenset(trans)]=1
return retDict
函数loadSimpDat()
把多条数据记录存储成列表的形式,函数createInitSet(dataSet)
把每条数据记录冻结(frozenset
函数)后作为字典的键,而每个键对应的值都是1
。
我们运行一下看看结果。
simpDat=loadSimpDat()
simpDat
initSet=createInitSet(simpDat)
initSet
第一次遍历数据集
FP
树第一次扫描数据库是为了获得每个元素项的出现频率。实现这一步的代码如下(注:代码中的dataSet
是经过上面所说的createInitSet(dataSet)
函数处理后的数据结果,即一个字典结构):
headerTable={}#用来存储每项元素及其出现次数
for trans in dataSet:#遍历每条记录
for item in trans:#遍历每条记录的每项元素
headerTable[item]=headerTable.get(item,0)+dataSet[trans]#计算每项元素的出现次数
print headerTable
print ("headerTable's length: %s" % len(headerTable))
这时headerTable
包含17
个元素:
接下来,去掉不满足最小支持度的元素项。
for k in headerTable.keys():
if headerTable[k]<3:#这里的3是指最小支持度的取值,可根据实际情况改变
del(headerTable[k])#如果某项元素的支持度小于最小支持度,从headerTable中删掉该元素
freqItemSet=set(headerTable.keys())#freqItemSet中的每一项元素的支持度均大于或等于最小支持度
print headerTable
print ("headerTable's length: %s" % len(headerTable))
存储符合条件的元素
freqItemSet
存储的都是符合条件的元素。如果freqItemSet
非空,证明确实有符合条件的元素。从前面的运行结果可以知道,headerTable
里面每一个键对应的值是该元素在所有的事务数据记录中出现的次数。现在,我们在每个键对应的值中增加一个“None”
,为后面的存储相似元素做准备。 if len(freqItemSet)!=0:
for k in headerTable:
headerTable[k]=[headerTable[k],None] headerTable
构建FP树
构建FP
树的思路是这样的:读入每个项集也就是每条记录,并将其添加到一条已经存在的路径中。如果该路径不存在,则创建一条新路径。假设有集合{z,x,y}
和{y,z,r}
,为了保证相同项只出现一次,需要对每条记录里的元素项进行排序。在每条记录中,这种排序是根据每个元素出现的次数进行的,也就是说出现次数越多,排位越前。
在对事务数据过滤和排序之后,就可以构建FP
树了。从空集(符号为Φ
)开始,依次遍历过滤,排序后的每一条记录,如果树中已经存在记录中的某个元素,那么已存在的元素增加值即可,如果树中不存在记录中的元素,那么增加一个分支。我们通过添加前两条事务为例,加深对建树的过程理解。
构建FP
树的思路是这样的:读入每个项集也就是每条记录,并将其添加到一条已经存在的路径中。如果该路径不存在,则创建一条新路径。假设有集合{z,x,y}
和{y,z,r}
,为了保证相同项只出现一次,需要对每条记录里的元素项进行排序。在每条记录中,这种排序是根据每个元素出现的次数进行的,也就是说出现次数越多,排位越前。
编程要求
请仔细阅读右侧代码,结合相关知识,在Begin-End
区域内进行代码补充,构建FP
树。
测试要求
测试程序会使用测试集对你编写的代码进行测试
开始你的任务吧,祝你成功!