原理:通过构建FP树,在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():
#self.children.values()是相当于treeNode类
#对不同层级的treeNode输出不同空格
child.disp(ind+1)
构建FP树
第一次遍历数据集先获得每个元素出现的频率,去掉不满足最小支持度的元素;然后构建FP树,读入每个项集,并将其添加到一条已存在的路径中,如果该路径不存在,则创建一条路径。在添加每个项集时,要先对项集中的元素按照频率由大到小排序。下图展示了将非频繁项移除并重排序后的事务数据集。
对事务记录过滤和排序后就可以创建FP树了,从空集开始,向其中不断添加频繁项集。如下图所示。
代码:
def createTree(dataSet,minSup=1):
headerTable={}
for trans in dataSet:
for item in trans:
#headerTable.get(item,0)返回指定键的值,没有返回0
headerTable[item]=headerTable.get(item,0)+dataSet[trans]
#移除不满足最小支持度的项
for k in list(headerTable.keys()):
if headerTable[k]<minSup:
del(headerTable[k])
freqItemSet=set(headerTable.keys())
#如果没有元素项满足要求则退出
if(len(freqItemSet)==0):
return None,None
#重新调整headerTable以使用Node Link
for k in headerTable:
headerTable[k]=[headerTable[k],None]
retTree=treeNode('Null Set',1,None)
for tranSet,count in dataSet.items():
localD={}
#对每个事务中的元素排序
for item in tranSet:
if item in freqItemSet:
localD[item]=headerTable[item][0]
if len(localD)>0:
orderedItems=[v[0] for v in sorted(localD.items(),key=lambda p:p[1],reverse=True)]
#使用排序后的频率项对树填充
#pdb.set_trace()
updateTree(orderedItems,retTree,headerTable,count)
return retTree,headerTable
def updateTree(items,inTree,headerTable,count):
if items[0] in inTree.children:
inTree.children[items[0]].inc(count)
else:
inTree.children[items[0]]=treeNode(items[0],count,inTree)
#更新头指针
if headerTable[items[0]][1]==None:
headerTable[items[0]][1]=inTree.children[items[0]]
else:
updateHeader(headerTable[items[0]][1],inTree.children[items[0]])
if(len(items)>1):
#对剩下的元素迭代调用updateTree函数
updateTree(items[1::], inTree.children[items[0]], headerTable, count)
def updateHeader(nodeToTest,targetNode):
while(nodeToTest.nodeLink!=None):
nodeToTest=nodeToTest.nodeLink
nodeToTest.nodeLink=targetNode