有了FP树之后,就可以抽取频繁项集了。这里的思路与Apriori算法大致类似,首先从单元素项集合开始,然后在此基础上逐步构建更大的集合。这里将利用FP树来做实现上述过程,不再需要原始数据集。
从FP树中抽取频繁项集的三个基本步骤如下:
1、从FP树中获得条件模式基;
2、利用条件模式基,构建一个条件FP树;
3、迭代重复步骤1、2,直到树包含一个元素项为止。
抽取条件模式基
首先从已经保存在头指针表中的单个频繁元素项开始。对于每一个元素项,获得其对应的条件模式基。条件模式基是以所查找元素项为结尾的路径集合。每一条路径其实都是一条前缀路径。简而言之,一条前缀路径是介于所查找元素项与树根节点之间的所有内容。
以之前的数据为例,符号r的前缀路径是{x,s}、{z,x,y}和{z}。每一条前缀路径都与一个计数值关联。该计数值等于起始元素项的计数值,该计数值给了每条路径上r的数目。下表列出了每个频繁项的所有前缀路径:
前缀路径将被用于构建条件FP树。为了获取这些前缀路径,可以对树进行穷举式搜索,直到获得想要的频繁项集为止,或者使用一个更有效的方法来加速搜索过程。可以利用先前创建的头指针表来得到一种更有效的方法。头指针表包含相同类型元素链表的起始指针。一旦到达了每一个元素项,就可以上溯这棵树直到根节点为止。
下面是前缀路径发现的代码:
def ascendTree(leafNode,prefixPath):
#迭代整棵树
if leafNode.parent != None:
prefixPath.append(leafNode.name)
ascendTree(leafNode.parent,prefixPath)
def findPrefixPath(basePat,treeNode):
condPats={}
while treeNode != None:
prefixPath=[]
ascendTree(treeNode,prefixPath)
if len(prefixPath)>1:
condPats[frozenset(prefixPath[1:])]=treeNode.count
treeNode = treeNode.nodeLink
return condPats
上述程序中的代码用于为给定元素项生成一个条件模式基,这通过访问树中所有包含给定元素项的节点来完成。当创建树的时候,使用头指针表来指向该类型的第一个元素项,该元素项也会链接到其后续元素项。函数findPrefixPath()遍历链表直到到达结尾。每遇到一个元素项都会调用ascendTree()来上溯FP树,并收集所有遇到的元素项的名称。该列表返回之后添加到条件模式基字典condPats中。
使用之前构建的树来看一下实际的运行效果:
print(findPrefixPath('x',myHeaderTab['x'][1]))
print(findPrefixPath('z',myHeaderTab['z'][1]))
print(findPrefixPath('r',myHeaderTab['r'][1]))
可以看到,运行结果和之前表中的结果是一致的。
有了条件模式基之后,就可以创建条件FP树。
创建条件FP树
对于每一个频繁项,都要创建一棵条件FP树。我们会为z、x以及其他频繁项构建条件树。可以使用刚才发现的条件模式基作为输入数据,并通过相同的建树代码来构建这些树。然后,我们会递归地发现频繁项、发现条件模式基,以及发现另外的条件树。举例来说,假定为频繁项t创建一个条件FP树,然后对{t,y}、{t,x}、……重复该过程。元素项t的条件FP树的构建过程如下所示:
在图中,可以注意到元素项s以及r是条件模式基的一部分,但是它们并不属于条件FP树。原因是单独来看它们都是频繁项,但是在t的条件树中,它们却不是频繁的,也就是说,{t,r}和{t,s}是不频繁的。
接下来,对集合{t,z},{t,x}以及{t,y}来挖掘对应的条件树。这会产生更复杂的频繁项集。该过程重复进行,直到条件树中没有元素为止,然后就可以停止了。具体代码实现:
def mineTree(inTree,headerTable,minSup,preFix,freqItemList):
#从头指针表的底端开始
bigL=[v[0] for v in sorted(headerTable.items(),key=lambda p: p[1])]
for basePat in bigL:
newFreqSet=preFix.copy()
newFreqSet.add(basePat)
freqItemList.append(newFreqSet)
condPattBases=findPrefixPath(basePat,headerTable[basePat][1])
#从条件模式基来构建条件FP树
myCondTree,myHead=createTree(condPattBases,minSup)
#挖掘条件FP树
if myHead !=None:
mineTree(myCondTree,myHead,minSup,newFreqSet,freqItemList)
创建条件树、前缀路径以及条件基的过程听起来比较复杂,但是代码实现起来相对简单。程序首先对头指针表中的元素项按照其出现的频率进行排序(从小到大)。然后,将每一个频繁项添加到频繁项集列表freqItemList中。接下来,递归调用程序findPrefixPath()函数来创建条件基。该条件基被当成一个新数据集输送给createTree()函数。这里为函数createTree()添加了足够的灵活性,以确保它可以被重用与构建条件树。最后,如果树中有元素项的话,递归调用mineTree()函数。
下面看运行效果:
建立一个空列表来存储所有的频繁项集,然后运行mineTree(),并显示出所有的条件树:
freqItems=[]
mineTree(myFPtree,myHeaderTab,3,set([]),freqItems)
为了得到上面的输出结果,在mineTree()函数中添加了两行:
print('conditional tree for: ', newFreqSet)
myCondTree.disp(1)
下面检查返回的项集与条件树是否匹配:
print(freqItems)