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 BeautifulSoup中文乱码问题的2种解决方法
Apr 22 Python
python基础教程之基本数据类型和变量声明介绍
Aug 29 Python
详解Django中的form库的使用
Jul 18 Python
Python tkinter实现的图片移动碰撞动画效果【附源码下载】
Jan 04 Python
Python线性方程组求解运算示例
Jan 17 Python
python自动截取需要区域,进行图像识别的方法
May 17 Python
pygame游戏之旅 载入小车图片、更新窗口
Nov 20 Python
python 定时任务去检测服务器端口是否通的实例
Jan 26 Python
python判断所输入的任意一个正整数是否为素数的两种方法
Jun 27 Python
pytorch加载自定义网络权重的实现
Jan 07 Python
python 中的paramiko模块简介及安装过程
Feb 29 Python
详解tensorflow之过拟合问题实战
Nov 01 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中字符串和正则表达式详解
2014/10/23 PHP
基于Web标准的UI组件 — 树状菜单(2)
2006/09/18 Javascript
(function(){})()的用法与优点
2007/03/11 Javascript
js 发个判断字符串是否为符合标准的函数
2009/04/27 Javascript
基于jQuery的倒计时实现代码
2012/05/30 Javascript
JavaScript中的noscript元素属性位置及作用介绍
2013/04/11 Javascript
提高jQuery性能的十个诀窍
2013/11/14 Javascript
Javascript 遍历页面text控件详解
2014/01/06 Javascript
JQuery复制DOM节点的方法
2015/06/11 Javascript
jQuery轻松实现表格的隔行变色和点击行变色的实例代码
2016/05/09 Javascript
EasyUI 中combotree 默认不能选择父节点的实现方法
2016/11/07 Javascript
jquery滚动条插件(可以自定义)
2016/12/11 Javascript
js实现类bootstrap模态框动画
2017/02/07 Javascript
基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果
2018/01/09 Javascript
微信小程序仿朋友圈发布动态功能
2018/07/15 Javascript
JavaScript求一个数组中重复出现次数最多的元素及其下标位置示例
2018/07/23 Javascript
Vue渲染过程浅析
2019/03/14 Javascript
火车票抢票python代码公开揭秘!
2018/03/08 Python
实践Vim配置python开发环境
2018/07/02 Python
Python OpenCV读取png图像转成jpg图像存储的方法
2018/10/28 Python
python实现定时压缩指定文件夹发送邮件
2020/12/22 Python
python 梯度法求解函数极值的实例
2019/07/10 Python
通过PHP与Python代码对比的语法差异详解
2019/07/10 Python
使用PyTorch训练一个图像分类器实例
2020/01/08 Python
移动web模拟客户端实现多方框输入密码效果【附代码】
2016/03/25 HTML / CSS
澳大利亚音乐商店:Bava’s Music City
2019/05/05 全球购物
安德玛加拿大官网:Under Armour加拿大
2019/10/02 全球购物
ORACLE第二个十问
2013/12/14 面试题
电子商务专业毕业生求职信
2014/06/12 职场文书
镇党政领导班子民主生活会思想汇报
2014/10/11 职场文书
护理见习报告范文
2014/11/03 职场文书
政府会议通知范文
2015/04/15 职场文书
读《庄子》有感:美而不自知
2019/11/06 职场文书
Python爬虫基础之简单说一下scrapy的框架结构
2021/06/26 Python
Python 全局空间和局部空间
2022/04/06 Python
在 Python 中利用 Pool 进行多线程
2022/04/24 Python