十一 python-FP-growth

Python 实现 FP-growth 的学习

1 FP-growth

FP-growth只需要对数据扫描两次,而Apriori需要对每个潜在的频繁项都会扫描一次数据,因此Apriori在大数据集中比较慢,而FP-growth速度较快

分为两步骤:1.将数据集构建为FP数,2.从FP树中挖掘频繁项

2 FP树的定义

FP树定义:

'''
FP树的定义
addnum 增加项出现次数的函数
display 显示FP树的结构
'''
class treeNode:
    def __init__(self,name,num,parent):
        self.name=name #项的名称
        self.num=num #项出现的次数
        self.parent=parent #项的父节点
        self.child={} #项的子节点
        self.link=None #项关联的节点,即与该项名称相同的节点

    def addnum(self,num):
        self.num+=num 

    def display(self,ind=1):
        print(' '*ind,self.name,' ',self.num)
        for child in self.child.values():
            child.display(ind+1)

原始数据如下:

对数据中出现的元素项进行扫描,获取每个元素的支持度
剔除其中支持度小于最小支持度的元素
最后,根据过滤之后元素的支持度,对事务进行重排序,支持度大的元素在前

然后根据重排后的事务,不断扩充FP树,过程如下:

将所有数据添加进入FP树:

FP树还需要一个头指针表来指向给定类型的第一个实例。利用头指针表,可快速访问FP树中一个给定类型的所有元素

并且在同一元素用 link指针连接

3 FP树的构建

'''
原始数据集
'''
def loaddata():
    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']]
    dataset={}
    for data in simpDat:
        dataset[frozenset(data)]=1
    return dataset

'''
根据数据集创建FP树
updatetree 用来将数据添加到FP树中
'''
def createtree(dataset,minsup=1):
    head={} #构建头指针表
    for data,num in dataset.items():
        for item in data:
            head[item]=head.get(item,0)+num
    a=list(head.keys()).copy()

    for k in a:
        if head[k]<minsup:
            del(head[k])
    freqset=set(head.keys()) #获得频繁项

    if len(freqset)==0:
        return None,None
    for key in head:
        head[key]=[head[key],None] #为指针留下存储空间

    mytree=treeNode('tree head',0,None) #定义FP树,根节点
    for data,num in dataset.items():
        tmpdata={}   #对数据进行过滤,只留下频繁项
        for item in data:   
            if item in freqset:
                tmpdata[item]=head[item][0]
        if len(tmpdata)>0: 
            order=[v[0] for v in sorted(tmpdata.items(),key=lambda p : p[1],reverse=True)]
            #根据支持度,对过滤后的数据的元素进行排序
            updatetree(order,mytree,head,num) 
    mytree.display() #显示FP树
    return mytree,head
'''
更新FP树
'''
def updatetree(item,intree,head,num):
    if item[0] in intree.child: #如果根节点的子节点中包含数据第一个元素,直接将该子节点的数量加一
        intree.child[item[0]].addnum(num) 
    else: #如果没有,则新创建一个子树,以该数据第一个元素作为根节点
        intree.child[item[0]]=treeNode(item[0],num,intree)

        if head[item[0]][1]==None: #确定头指针表中该元素,是否已经存在指针
            head[item[0]][1]=intree.child[item[0]] #如果没有相连,定义好指针
        else: #如果存在指针,证明该元素已经在FP树出现过,通过链表link找到最后一个出现该元素,并将最后元素与新出现元素相连
            cur=head[item[0]][1]
            while cur.link != None:
                cur=cur.link
            cur.link=intree.child[item[0]]

    if len(item)>1: #当数据的元素大于1,则对后面元素进行重复操作
        updatetree(item[1:],intree.child[item[0]],head,num)

测试

if __name__=='__main__':
    dataset=loaddata()
    mytree,head=createtree(dataset,3) 

结果

  tree head   0
   z   5
    r   1
    x   3
     t   3
      y   3
       s   2
       r   1
   x   1
    s   1
     r   1

4 从FP树中挖掘频繁项

过程:
从FP树中获取条件模式基
个根据条件模式基构建条件FP树
重复以上过程,直到树包含一个元素为止

从FP树中提取,头指针表中每个元素的前缀路径,通过指针找到FP树中对应项,然后向上传递直到找到根节点,并将对应项的数量作为前缀的数量

确定频繁项的前缀

扫描二维码关注公众号,回复: 1889107 查看本文章
'''
根据FP树,和头指针表,确定头指针表元素的前缀
'''
def findpath(tree,head):
    result={}
    for key in head:
        curlink=head[key][1] #将指针移动到第一个对应元素
        tmp={}      #创建字典存储前缀
        while curlink != None:
            cur=curlink    
            num=cur.num
            path=[]
            while cur.parent.name !='tree head':
                cur=cur.parent
                path.append(cur.name)


            if len(path)!=0:
                tmp[frozenset(path)]=num            
            curlink=curlink.link        
        result[key]=tmp

    return result

构建条件FP树,并挖掘频繁项

'''
根据频繁项的前缀确定所有频繁项
'''
def minetree(intree,head,minsup,pre,freqlist,ind):
    path=findpath(intree,head)  
    #获得频繁项的前缀  
    bigl=[v[0] for v in sorted(head.items(),key=lambda p:p[1][0])]
    #根据支持度对头指针表排序,从小到大
    for base in bigl:
        #将已经用过的前缀复制,并加入当前头指针表元素,构建新频繁项
        newpre=pre.copy() 
        newpre.add(base)
        freqlist.append(newpre)

        #将前缀作为数据,构建FP树,获得新的头指针表
        basetree,basehead=createtree(path[base],minsup)

        if basehead!=None:
            #递归调用 直到无法再构建条件FP树
            minetree(basetree,basehead,minsup,newpre,freqlist,ind+1)

测试

dataset=loaddata()
    mytree,head=createtree(dataset,3)

    freqlist=[]
    pre=set([])
    ind=1
    minetree(mytree,head,3,pre,freqlist,ind)
    for item in freqlist:
        print(item)
    print(len(freqlist))

结果

  tree head   0
   z   5
    r   1
    x   3
     t   3
      y   3
       s   2
       r   1
   x   1
    s   1
     r   1
  tree head   0
   z   3
    x   3
  tree head   0
   z   3
  tree head   0
   x   3
    z   3
     t   3
  tree head   0
   x   3
  tree head   0
   z   3
    x   3
  tree head   0
   z   3
  tree head   0
   x   3
  tree head   0
   z   3
{'r'}
{'t'}
{'z', 't'}
{'x', 't'}
{'x', 'z', 't'}
{'y'}
{'x', 'y'}
{'z', 'y'}
{'z', 'x', 'y'}
{'t', 'y'}
{'z', 't', 'y'}
{'x', 't', 'y'}
{'x', 'z', 't', 'y'}
{'s'}
{'s', 'x'}
{'x'}
{'z', 'x'}
{'z'}
18

完整代码


'''
FP树的定义
addnum 增加项出现次数的函数
display 显示FP树的结构
'''
class treeNode:
    def __init__(self,name,num,parent):
        self.name=name #项的名称
        self.num=num #项出现的次数
        self.parent=parent #项的父节点
        self.child={} #项的子节点
        self.link=None #项关联的节点,即与该项名称相同的节点

    def addnum(self,num):
        self.num+=num 

    def display(self,ind=1):
        print(' '*ind,self.name,' ',self.num)
        for child in self.child.values():
            child.display(ind+1)

'''
原始数据集
'''
def loaddata():
    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']]
    dataset={}
    for data in simpDat:
        dataset[frozenset(data)]=1
    return dataset

'''
根据数据集创建FP树
updatetree 用来将数据添加到FP树中
'''
def createtree(dataset,minsup=1):
    head={} #构建头指针表
    for data,num in dataset.items():
        for item in data:
            head[item]=head.get(item,0)+num
    a=list(head.keys()).copy()

    for k in a:
        if head[k]<minsup:
            del(head[k])
    freqset=set(head.keys()) #获得频繁项

    if len(freqset)==0:
        return None,None
    for key in head:
        head[key]=[head[key],None] #为指针留下存储空间

    mytree=treeNode('tree head',0,None) #定义FP树,根节点
    for data,num in dataset.items():
        tmpdata={}   #对数据进行过滤,只留下频繁项
        for item in data:   
            if item in freqset:
                tmpdata[item]=head[item][0]
        if len(tmpdata)>0: 
            order=[v[0] for v in sorted(tmpdata.items(),key=lambda p : p[1],reverse=True)]
            #根据支持度,对过滤后的数据的元素进行排序
            updatetree(order,mytree,head,num) 
    mytree.display() #显示FP树
    return mytree,head
'''
更新FP树
'''
def updatetree(item,intree,head,num):
    if item[0] in intree.child: #如果根节点的子节点中包含数据第一个元素,直接将该子节点的数量加一
        intree.child[item[0]].addnum(num) 
    else: #如果没有,则新创建一个子树,以该数据第一个元素作为根节点
        intree.child[item[0]]=treeNode(item[0],num,intree)

        if head[item[0]][1]==None: #确定头指针表中该元素,是否已经存在指针
            head[item[0]][1]=intree.child[item[0]] #如果没有相连,定义好指针
        else: #如果存在指针,证明该元素已经在FP树出现过,通过链表link找到最后一个出现该元素,并将最后元素与新出现元素相连
            cur=head[item[0]][1]
            while cur.link != None:
                cur=cur.link
            cur.link=intree.child[item[0]]

    if len(item)>1: #当数据的元素大于1,则对后面元素进行重复操作
        updatetree(item[1:],intree.child[item[0]],head,num)
'''
根据FP树,和头指针表,确定头指针表元素的前缀
'''
def findpath(tree,head):
    result={}
    for key in head:
        curlink=head[key][1] #将指针移动到第一个对应元素
        tmp={}      #创建字典存储前缀
        while curlink != None:
            cur=curlink    
            num=cur.num
            path=[]
            while cur.parent.name !='tree head':
                cur=cur.parent
                path.append(cur.name)


            if len(path)!=0:
                tmp[frozenset(path)]=num            
            curlink=curlink.link        
        result[key]=tmp

    return result
'''
根据频繁项的前缀确定所有频繁项
'''
def minetree(intree,head,minsup,pre,freqlist,ind):
    path=findpath(intree,head)  
    #获得频繁项的前缀  
    bigl=[v[0] for v in sorted(head.items(),key=lambda p:p[1][0])]
    #根据支持度对头指针表排序,从小到大
    for base in bigl:
        #将已经用过的前缀复制,并加入当前头指针表元素,构建新频繁项
        newpre=pre.copy() 
        newpre.add(base)
        freqlist.append(newpre)

        #将前缀作为数据,构建FP树,获得新的头指针表
        basetree,basehead=createtree(path[base],minsup)

        if basehead!=None:
            #递归调用 直到无法再构建条件FP树
            minetree(basetree,basehead,minsup,newpre,freqlist,ind+1)
def test1():
    dataset=loaddata()
    mytree,head=createtree(dataset,3)

    freqlist=[]
    pre=set([])
    ind=1
    minetree(mytree,head,3,pre,freqlist,ind)
    for item in freqlist:
        print(item)
    print(len(freqlist))

if __name__=='__main__':
    test1()

猜你喜欢

转载自blog.csdn.net/qq_35282560/article/details/79469311