zoukankan      html  css  js  c++  java
  • 机器学习实战 9-FP-growth算法

    1 FP树

    1.1  FP介绍

    将数据结构存储在一种称为FP树的紧凑数据结构中,如图:

     

    一般流程:

    1. 输入数据,构建出一个如上图所示的数据结构(可以理解为按每条数据集合一步一步建立出来的树),即FP树

    2. 从FP树中挖掘频繁项集

      1. 从FP树中获得条件模式基(以某查找元素为结尾的路径集合,如y元素的条件模式基为{[t,s,x,z],[t,r,x,z]})

      2. 利用条件模式基再构建条件FP树(这里是有频次阈值要求的)

      3. 迭代 a,b ,直到树中只包含一个元素为止。此时已经找到满足阈值条件所有频繁项集。

    (阈值要求:即频繁次数的最低要求,如要求某元素或者集合至少出现100次)

    1.2  与Apriori对比

    之前学的关联算法是Apriori,它对每个潜在的频繁项集都会扫描数据集判定给定模式是否频繁,而此处介绍的FP-growth只需对数据库进行两次扫描,因此较快。当输入数据量比较大时,FP树优势较明显

    2 python实现及图解

    2.1 创建FP树的数据结构

    包含存放节点的名字及计数值,nodeLink用于存放链接元素。

     1 # 建立FP树的类定义
     2 class treeNode:
     3     def __init__(self,nameValue,numOccur,parrentNode):
     4         self.name=nameValue            # 存放节点名字
     5         self.count=numOccur            # 存放计数值
     6         self.nodeLink=None             # 链接相似的元素项
     7         self.parent=parrentNode        # 指向父节点
     8         self.children={}               # 存放子节点
     9     def inc(self,numOccur):
    10         self.count +=numOccur
    11     def disp(self,ind=1):
    12         print(' '*ind,self.name,' ',self.count)
    13         for child in self.children.values():
    14             child.disp(ind+1)

    2.2 构建FP树

    算法过程:

      1. 统计所有元素出现的频次,并删去不满足阈值的元素

      2. 创建一个头指针表headerTable,用来存放各元素总数,并存放指针(即位置)

      3. 遍历每一个集合

        1. 选取集合中满足要求的元素,并将它们顺序按照总频数从多到少的排列(orderedItem),从上往下建立树,并往headerTable中填充指针

        2. 当headerTable中某一元素已经含有指针时,如遍历到第四次时的x,此时利用updateHeader函数,用nodeLink链接(图4中x到x的蓝色虚线)

    程序如下:

     1 # 建立FP树的类定义
     2 class treeNode:
     3     def __init__(self,nameValue,numOccur,parrentNode):
     4         self.name=nameValue            # 存放节点名字
     5         self.count=numOccur            # 存放计数值
     6         self.nodeLink=None             # 链接相似的元素项
     7         self.parent=parrentNode        # 指向父节点
     8         self.children={}               # 存放子节点
     9 
    10     def inc(self,numOccur):
    11         self.count +=numOccur
    12 
    13     def disp(self,ind=1):
    14         print(' '*ind,self.name,' ',self.count)
    15         for child in self.children.values():
    16             child.disp(ind+1)
    17 
    18 # FP树构建函数
    19 def createTree(dataSet, minSup=1):
    20     # 使用字典作为数据结构,保存头指针
    21     headerTable={}
    22     # 遍历数据集,获得每个元素的出现频率
    23     for trans in dataSet:
    24         # print(trans)
    25         for item in trans:
    26             headerTable[item]=headerTable.get(item,0)+dataSet[trans]
    27     # 删去出现频率少的小伙伴
    28     for k in list(headerTable.keys()):
    29         if headerTable[k]<minSup:
    30             del(headerTable[k])
    31     freqItemSet=set(headerTable.keys())
    32     if len(freqItemSet)==0:
    33         return None,None
    34     for k in headerTable:
    35         headerTable[k]=[headerTable[k],None]
    36     reTree=treeNode('Null Set',1,None)
    37     for tranSet,count in dataSet.items():
    38         localD={}
    39         for item in tranSet:
    40             if item in freqItemSet:
    41                 localD[item]=headerTable[item][0]
    42         if len(localD)>0:
    43             # 根据出现的频次对localD进行排序
    44             orderdItems=[v[0] for v in sorted(localD.items(),key=lambda p:p[1],reverse=True)]
    45             print(orderdItems)
    46             updateTree(orderdItems,reTree,headerTable,count)
    47     return reTree,headerTable
    48 
    49 def updateTree(items,inTree,headerTable,count):
    50     # 判断是否子节点,如果是,更新计数,如果不是创建子节点
    51     if items[0] in inTree.children:
    52         inTree.children[items[0]].inc(count)
    53     else:
    54         inTree.children[items[0]]=treeNode(items[0],count,inTree)
    55         if headerTable[items[0]][1]==None:
    56             headerTable[items[0]][1]=inTree.children[items[0]]
    57         else:
    58             updateHeader(headerTable[items[0]][1],inTree.children[items[0]])
    59     # 循环创建
    60     if len(items)>1:
    61         updateTree(items[1::],inTree.children[items[0]],headerTable,count)
    62  # 更新指针链接
    63 def updateHeader(nodeToTest,targetNode):
    64     while (nodeToTest.nodeLink != None):
    65         nodeToTest=nodeToTest.nodeLink
    66     nodeToTest.nodeLink=targetNode
    67 
    68 def loadSimpDat():
    69     simpDat = [['r', 'z', 'h', 'j', 'p'],
    70                ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],
    71                ['z'],
    72                ['r', 'x', 'n', 'o', 's'],
    73                ['y', 'r', 'x', 'z', 'q', 't', 'p'],
    74                ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
    75     return simpDat
    76 
    77 # 将输入数据列表转换为字典
    78 def createInitSet(dataSet):
    79     retDict = {}
    80     for trans in dataSet:
    81         retDict[frozenset(trans)] = 1
    82     return retDict
    83 
    84 simpDat=loadSimpDat()
    85 # print(simpDat)
    86 initSet=createInitSet(simpDat)
    87 # print(initSet)
    88 myFPtree,myHeaderTab=createTree(initSet,3)
    89 a=myFPtree.disp()
    90 print(myHeaderTab)
    View Code

    图解:

    注意:

      1. 每一次都是从根本节点“Null Set”开始,往下建立

      2. 注意nodeLink保存位置,主要由updateHeader函数起作用

      3. 由于频次相同的元素较多(如r,t,y,s),每次生成树可能不太一样

    2.3 抽取条件模式基

    前面说过,条件模式基就是以所查找元素为结尾的路径集合。即以某元素为起点,往上追溯的路径集合。如上面 t 的条件模式基为 {{s,x,z}:2,{r,x,z}:1} 。

    程序:

     1 # 递归回溯树,从本节点一直往上
     2 def ascendTree(leafNode,prefixPath):
     3     if leafNode.parent != None:
     4         prefixPath.append(leafNode.name)
     5         ascendTree(leafNode.parent,prefixPath)
     6 
     7 # 找到需查找元素的所有前缀路径
     8 def findPrefixPath(basePat,treeNode):
     9     condPats={}
    10     while treeNode != None:
    11         prefixPath=[]
    12         ascendTree(treeNode,prefixPath)
    13         if len(prefixPath)>1:
    14             condPats[frozenset(prefixPath[1:])]=treeNode.count
    15         treeNode=treeNode.nodeLink
    16     return condPats
    17 
    18 simpDat=loadSimpDat()
    19 # print(simpDat)
    20 initSet=createInitSet(simpDat)
    21 # print(initSet)
    22 myFPtree,myHeaderTab=createTree(initSet,3)
    23 a=myFPtree.disp()
    24 print(myHeaderTab)
    25 print(myFPtree)
    26 for item in list(myHeaderTab.keys()):
    27     print(findPrefixPath(item,myHeaderTab[item][1]))
    View Code

    2.4 创建条件FP树

    利用条件模式基,创建FP树,........循环,直到树中只包含一个元素,这就是一个找频繁项集的过程(一元的、二元的、.....等等多元的,各种类型的,所有满足条件的组合)

     1 # 创建条件FP树
     2 def mineTree(inTree,headerTable,minSup,preFix,freqItemList):
     3     # 此处原文有错误
     4     # 从头指针的底端开始
     5     bigL=[v[0] for v in sorted(headerTable.items(),key=lambda p:p[0])]
     6     for basePat in bigL:
     7         newFreqSet=preFix.copy()
     8         newFreqSet.add(basePat)
     9         freqItemList.append(newFreqSet)
    10         # 创建条件基
    11         condPattBases=findPrefixPath(basePat,headerTable[basePat][1])
    12         # 创造条件fp树
    13         myCondTree,myHead=createTree(condPattBases,minSup)
    14         # 递归找到满足阈值的项
    15         # myHead==None 时,说明树只有一层叶节点,即频繁次项为一元 
    16         if myHead!=None:
    17             print('conditional tree for:',newFreqSet)
    18             myCondTree.disp(1)
    19             mineTree(myCondTree,myHead,minSup,newFreqSet,freqItemList)
    20             
    21 freqItems=[]
    22 c=mineTree(myFPtree,myHeaderTab,3,set([]),freqItems)
    23 print(freqItems)          
    View Code

     

    3 实例:从新闻网站点击流中挖掘

      1 # 1. 建立FP树的类定义
      2 class treeNode:
      3     def __init__(self,nameValue,numOccur,parrentNode):
      4         self.name=nameValue            # 存放节点名字
      5         self.count=numOccur            # 存放计数值
      6         self.nodeLink=None             # 链接相似的元素项
      7         self.parent=parrentNode        # 指向父节点
      8         self.children={}               # 存放子节点
      9 
     10     def inc(self,numOccur):
     11         self.count +=numOccur
     12 
     13     def disp(self,ind=1):
     14         print(' '*ind,self.name,' ',self.count)
     15         for child in self.children.values():
     16             child.disp(ind+1)
     17             
     18 # 2. 将输入数据列表转换为字典
     19 def createInitSet(dataSet):
     20     retDict = {}
     21     for trans in dataSet:
     22         retDict[frozenset(trans)] = 1
     23     return retDict            
     24 
     25 # 3. 构建FP树
     26 def createTree(dataSet, minSup=1):
     27     # 使用字典作为数据结构,保存头指针
     28     headerTable={}
     29     # 遍历数据集,获得每个元素的出现频率
     30     for trans in dataSet:
     31         # print(trans)
     32         for item in trans:
     33             headerTable[item]=headerTable.get(item,0)+dataSet[trans]
     34     # 删去出现频率少的小伙伴
     35     for k in list(headerTable.keys()):
     36         if headerTable[k]<minSup:
     37             del(headerTable[k])
     38     freqItemSet=set(headerTable.keys())
     39     if len(freqItemSet)==0:
     40         return None,None
     41     for k in headerTable:
     42         headerTable[k]=[headerTable[k],None]
     43     reTree=treeNode('Null Set',1,None)
     44     for tranSet,count in dataSet.items():
     45         localD={}
     46         for item in tranSet:
     47             if item in freqItemSet:
     48                 localD[item]=headerTable[item][0]
     49         if len(localD)>0:
     50             # 根据出现的频次对localD进行排序
     51             orderdItems=[v[0] for v in sorted(localD.items(),key=lambda p:p[1],reverse=True)]
     52             # print(orderdItems)
     53             updateTree(orderdItems,reTree,headerTable,count)
     54     return reTree,headerTable
     55 def updateTree(items,inTree,headerTable,count):
     56     # 判断是否子节点,如果是,更新计数,如果不是创建子节点
     57     if items[0] in inTree.children:
     58         inTree.children[items[0]].inc(count)
     59     else:
     60         inTree.children[items[0]]=treeNode(items[0],count,inTree)
     61         if headerTable[items[0]][1]==None:
     62             headerTable[items[0]][1]=inTree.children[items[0]]
     63         else:
     64             updateHeader(headerTable[items[0]][1],inTree.children[items[0]])
     65     # 循环创建
     66     if len(items)>1:
     67         updateTree(items[1::],inTree.children[items[0]],headerTable,count)
     68  # 更新指针链接
     69 def updateHeader(nodeToTest,targetNode):
     70     while (nodeToTest.nodeLink != None):
     71         nodeToTest=nodeToTest.nodeLink
     72     nodeToTest.nodeLink=targetNode
     73     
     74 # 4. 构建条件模式基
     75 # 递归回溯树,从本节点一直往上
     76 def ascendTree(leafNode,prefixPath):
     77     if leafNode.parent != None:
     78         prefixPath.append(leafNode.name)
     79         ascendTree(leafNode.parent,prefixPath)
     80 # 找到需查找元素的所有前缀路径
     81 def findPrefixPath(basePat,treeNode):
     82     condPats={}
     83     while treeNode != None:
     84         prefixPath=[]
     85         ascendTree(treeNode,prefixPath)
     86         if len(prefixPath)>1:
     87             condPats[frozenset(prefixPath[1:])]=treeNode.count
     88         treeNode=treeNode.nodeLink
     89     return condPats    
     90 
     91 #  5.创建条件FP树,找到频繁项
     92 def mineTree(inTree,headerTable,minSup,preFix,freqItemList):
     93     # 此处原文有错误
     94     # 从头指针的底端开始
     95     bigL=[v[0] for v in sorted(headerTable.items(),key=lambda p:p[0])]
     96     for basePat in bigL:
     97         newFreqSet=preFix.copy()
     98         newFreqSet.add(basePat)
     99         freqItemList.append(newFreqSet)
    100         # 创建条件基
    101         condPattBases=findPrefixPath(basePat,headerTable[basePat][1])
    102         # 创造条件fp树
    103         myCondTree,myHead=createTree(condPattBases,minSup)
    104         # 递归找到满足阈值的项
    105         # myHead==None 时,说明树只有一层叶节点,即频繁次项为一元
    106         if myHead!=None:
    107             print('conditional tree for:',newFreqSet)
    108             myCondTree.disp(1)
    109             mineTree(myCondTree,myHead,minSup,newFreqSet,freqItemList)
    110 
    111 parseDat=[line.split() for line in open('kosarak.dat').readlines()]
    112 initSet=createInitSet(parseDat)
    113 myFPtree,myHeaderTab=createTree(initSet,100000)
    114 myFreqlist=[]
    115 mineTree(myFPtree,myHeaderTab,100000,set([]),myFreqlist)
    116 print(myFreqlist)            
    View Code
    大概运行了8秒钟,得到:

     
     
     
     
     
     
  • 相关阅读:
    Redis学习笔记
    Springboot + Tomcat跑项目出现端口被占用的问题
    按层打印二叉树
    打印二叉树的镜像——剑指offer
    判断树的子结构——剑指offer
    实习半个月的感想
    使用KMP算法判断是否为旋转词
    微信双开
    win10 右键添加cmd当前目录打开
    勒索邮件
  • 原文地址:https://www.cnblogs.com/Ray-0808/p/10966321.html
Copyright © 2011-2022 走看看