FP-growth算法发现频繁项集——构建FP树


Posted in Python onJune 24, 2021

  FP代表频繁模式(Frequent Pattern),算法主要分为两个步骤:FP-tree构建、挖掘频繁项集。

FP树表示法

  FP树通过逐个读入事务,并把事务映射到FP树中的一条路径来构造。由于不同的事务可能会有若干个相同的项,因此它们的路径可能部分重叠。路径相互重叠越多,使用FP树结构获得的压缩效果越好;如果FP树足够小,能够存放在内存中,就可以直接从这个内存中的结构提取频繁项集,而不必重复地扫描存放在硬盘上的数据。

  一颗FP树如下图所示:

FP-growth算法发现频繁项集——构建FP树

  通常,FP树的大小比未压缩的数据小,因为数据的事务常常共享一些共同项,在最好的情况下,所有的事务都具有相同的项集,FP树只包含一条节点路径;当每个事务都具有唯一项集时,导致最坏情况发生,由于事务不包含任何共同项,FP树的大小实际上与原数据的大小一样。

  FP树的根节点用φ表示,其余节点包括一个数据项和该数据项在本路径上的支持度;每条路径都是一条训练数据中满足最小支持度的数据项集;FP树还将所有相同项连接成链表,上图中用蓝色连线表示。

  为了快速访问树中的相同项,还需要维护一个连接具有相同项的节点的指针列表(headTable),每个列表元素包括:数据项、该项的全局最小支持度、指向FP树中该项链表的表头的指针。

FP-growth算法发现频繁项集——构建FP树

构建FP树

  现在有如下数据:

FP-growth算法发现频繁项集——构建FP树  

FP-growth算法需要对原始训练集扫描两遍以构建FP树。

  第一次扫描,过滤掉所有不满足最小支持度的项;对于满足最小支持度的项,按照全局最小支持度排序,在此基础上,为了处理方便,也可以按照项的关键字再次排序。

FP-growth算法发现频繁项集——构建FP树

第一次扫描的后的结果

  第二次扫描,构造FP树。

  参与扫描的是过滤后的数据,如果某个数据项是第一次遇到,则创建该节点,并在headTable中添加一个指向该节点的指针;否则按路径找到该项对应的节点,修改节点信息。具体过程如下所示:

FP-growth算法发现频繁项集——构建FP树

事务001,{z,x}

FP-growth算法发现频繁项集——构建FP树

事务002,{z,x,y,t,s}

FP-growth算法发现频繁项集——构建FP树

事务003,{z}

FP-growth算法发现频繁项集——构建FP树

事务004,{x,s,r}

FP-growth算法发现频繁项集——构建FP树

事务005,{z,x,y,t,r}

FP-growth算法发现频繁项集——构建FP树

事务006,{z,x,y,t,s}

  从上面可以看出,headTable并不是随着FPTree一起创建,而是在第一次扫描时就已经创建完毕,在创建FPTree时只需要将指针指向相应节点即可。从事务004开始,需要创建节点间的连接,使不同路径上的相同项连接成链表。

  代码如下:

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 = {}
    for trans in dataSet:
        fset = frozenset(trans)
        retDict.setdefault(fset, 0)
        retDict[fset] += 1
    return retDict
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)

def createTree(dataSet, minSup=1):
    headerTable = {}
    #此一次遍历数据集, 记录每个数据项的支持度
    for trans in dataSet:
        for item in trans:
            headerTable[item] = headerTable.get(item, 0) + 1
    #根据最小支持度过滤
    lessThanMinsup = list(filter(lambda k:headerTable[k] < minSup, headerTable.keys()))
    for k in lessThanMinsup: del(headerTable[k])
    freqItemSet = set(headerTable.keys())
    #如果所有数据都不满足最小支持度,返回None, None
    if len(freqItemSet) == 0:
        return None, None
    for k in headerTable:
        headerTable[k] = [headerTable[k], None]
    retTree = treeNode('φ', 1, None)
    #第二次遍历数据集,构建fp-tree
    for tranSet, count in dataSet.items():
        #根据最小支持度处理一条训练样本,key:样本中的一个样例,value:该样例的的全局支持度
        localD = {}
        for item in tranSet:
            if item in freqItemSet:
                localD[item] = headerTable[item][0]
        if len(localD) > 0:
            #根据全局频繁项对每个事务中的数据进行排序,等价于 order by p[1] desc, p[0] desc
            orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: (p[1],p[0]), reverse=True)]
            updateTree(orderedItems, retTree, headerTable, count)
    return retTree, headerTable

def updateTree(items, inTree, headerTable, count):
    if items[0] in inTree.children:  # check if orderedItems[0] in retTree.children
        inTree.children[items[0]].inc(count)  # incrament count
    else:  # add items[0] to inTree.children
        inTree.children[items[0]] = treeNode(items[0], count, inTree)
        if headerTable[items[0]][1] == None:  # update header table
            headerTable[items[0]][1] = inTree.children[items[0]]
        else:
            updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
    if len(items) > 1:  # call updateTree() with remaining ordered items
        updateTree(items[1:], inTree.children[items[0]], headerTable, count)

def updateHeader(nodeToTest, targetNode):  # this version does not use recursion
    while (nodeToTest.nodeLink != None):  # Do not use recursion to traverse a linked list!
        nodeToTest = nodeToTest.nodeLink
    nodeToTest.nodeLink = targetNode
simpDat = loadSimpDat()
dictDat = createInitSet(simpDat)
myFPTree,myheader = createTree(dictDat, 3)
myFPTree.disp()

  上面的代码在第一次扫描后并没有将每条训练数据过滤后的项排序,而是将排序放在了第二次扫描时,这可以简化代码的复杂度。

  控制台信息:

FP-growth算法发现频繁项集——构建FP树

项的顺序对FP树的影响

  值得注意的是,对项的关键字排序将会影响FP树的结构。下面两图是相同训练集生成的FP树,图1除了按照最小支持度排序外,未对项做任何处理;图2则将项按照关键字进行了降序排序。树的结构也将影响后续发现频繁项的结果。

FP-growth算法发现频繁项集——构建FP树

图1 未对项的关键字排序

FP-growth算法发现频繁项集——构建FP树

图2 对项的关键字降序排序

总结  

本派文章就到这里了,下篇继续,介绍如何发现频繁项集。希望能给你带来帮助,也希望您能够多多关注三水点靠木的更多内容!

Python 相关文章推荐
两个使用Python脚本操作文件的小示例分享
Aug 27 Python
python中urlparse模块介绍与使用示例
Nov 19 Python
Python使用pip安装pySerial串口通讯模块
Apr 20 Python
pyqt5 实现 下拉菜单 + 打开文件的示例代码
Jun 20 Python
python字典改变value值方法总结
Jun 21 Python
python 实现12bit灰度图像映射到8bit显示的方法
Jul 08 Python
pymysql模块的操作实例
Dec 17 Python
Windows下实现将Pascal VOC转化为TFRecords
Feb 17 Python
python 成功引入包但无法正常调用的解决
Mar 09 Python
pip安装tensorflow的坑的解决
Apr 19 Python
爬虫代理的cookie如何生成运行
Sep 22 Python
python 基于pygame实现俄罗斯方块
Mar 02 Python
python ansible自动化运维工具执行流程
关于python中readlines函数的参数hint的相关知识总结
详解Python为什么不用设计模式
linux中nohup和后台运行进程查看及终止
Jun 24 #Python
Python面向对象之成员相关知识总结
Jun 24 #Python
Python面向对象之内置函数相关知识总结
Jun 24 #Python
python面向对象版学生信息管理系统
You might like
发布一个迷你php+AJAX聊天程序[聊天室]提供下载
2007/07/21 PHP
在WINDOWS中设置计划任务执行PHP文件的方法
2011/12/19 PHP
thinkphp实现发送邮件密码找回功能实例
2014/12/01 PHP
Yii清理缓存的方法
2016/01/06 PHP
用js实现的检测浏览器和系统的函数
2009/04/09 Javascript
jquery validate使用攻略 第四步
2010/07/01 Javascript
jquery关于页面焦点的定位(文本框获取焦点时改变样式 )
2010/09/10 Javascript
javascipt匹配单行和多行注释的正则表达式
2013/11/20 Javascript
通过实例理解javascript中没有函数重载的概念
2015/06/03 Javascript
JS实现的简洁二级导航菜单雏形效果
2015/10/13 Javascript
vuejs手把手教你写一个完整的购物车实例代码
2017/07/06 Javascript
jQuery选取所有复选框被选中的值并用Ajax异步提交数据的实例
2017/08/04 jQuery
微信小程序实现团购或秒杀批量倒计时
2020/11/01 Javascript
浅谈webpack devtool里的7种SourceMap模式
2019/01/14 Javascript
vue实现短信验证码登录功能(流程详解)
2019/12/10 Javascript
vue插槽slot的简单理解与用法实例分析
2020/03/14 Javascript
JS 获取文件后缀,判断文件类型(比如是否为图片格式)
2020/05/09 Javascript
html中创建并调用vue组件的几种方法汇总
2020/11/17 Javascript
深入理解python中的闭包和装饰器
2016/06/12 Python
Python实现购物车功能的方法分析
2017/11/10 Python
Python FTP两个文件夹间的同步实例代码
2018/05/25 Python
Golang GBK转UTF-8的例子
2019/08/26 Python
pycharm 快速解决python代码冲突的问题
2021/01/15 Python
美国鞋类购物网站:Shiekh Shoes
2016/08/21 全球购物
世界上最大的汽车共享网站:Zipcar
2017/01/14 全球购物
英国经典球衣网站:Classic Football Shirts
2017/05/20 全球购物
String和StringBuffer的区别
2015/08/13 面试题
在SQL Server中创建数据库主要有那种方式
2013/09/10 面试题
房地产项目策划书
2014/02/05 职场文书
家电业务员岗位职责
2014/03/10 职场文书
党员公开承诺书内容
2014/05/20 职场文书
优秀班组长事迹
2014/05/31 职场文书
警察群众路线整改措施
2014/09/26 职场文书
2015初中政治教学工作总结
2015/07/21 职场文书
python实现Thrift服务端的方法
2021/04/20 Python
Python中threading库实现线程锁与释放锁
2021/05/17 Python