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连接mysql数据库示例(做增删改操作)
Dec 31 Python
python中的全局变量用法分析
Jun 09 Python
Python三级目录展示的实现方法
Sep 28 Python
django使用html模板减少代码代码解析
Dec 12 Python
Python中XlsxWriter模块简介与用法分析
Apr 24 Python
详解Python requests 超时和重试的方法
Dec 18 Python
python爬虫中多线程的使用详解
Sep 23 Python
Python面向对象原理与基础语法详解
Jan 02 Python
Python实现实时数据采集新型冠状病毒数据实例
Feb 04 Python
Python日志打印里logging.getLogger源码分析详解
Jan 17 Python
详解分布式系统中如何用python实现Paxos
May 18 Python
python标准库ElementTree处理xml
May 20 Python
python ansible自动化运维工具执行流程
关于python中readlines函数的参数hint的相关知识总结
详解Python为什么不用设计模式
linux中nohup和后台运行进程查看及终止
Jun 24 #Python
Python面向对象之成员相关知识总结
Jun 24 #Python
Python面向对象之内置函数相关知识总结
Jun 24 #Python
python面向对象版学生信息管理系统
You might like
discuz7 phpMysql操作类
2009/06/21 PHP
深入PHP nl2br()格式化输出的详解
2013/06/05 PHP
PHP文件缓存类实现代码
2015/10/26 PHP
php实现获取近几日、月时间示例
2019/07/06 PHP
alixixi runcode.asp的代码不错的应用
2007/08/08 Javascript
ie与ff下的event事件使用介绍
2013/11/25 Javascript
javascript中数组的定义及使用实例
2015/01/21 Javascript
jQuery仿gmail实现fixed布局的方法
2015/05/27 Javascript
js实现YouKu的漂亮搜索框效果
2015/08/19 Javascript
jQuery实现的导航动画效果(附demo源码)
2016/04/01 Javascript
jquery拖动层效果插件用法实例分析(附demo源码)
2016/04/28 Javascript
jQuery EasyUI Pagination实现分页的常用方法
2016/05/21 Javascript
AngularJS上拉加载问题解决方法
2016/05/23 Javascript
javascript中使用未定义变量或值的情况分析
2016/07/19 Javascript
Jquery删除css属性的简单方法
2016/12/04 Javascript
基于Two.js实现星球环绕动画效果的示例
2017/11/06 Javascript
深入浅析Vue全局组件与局部组件的区别
2018/06/15 Javascript
angular 服务的单例模式(依赖注入模式下)详解
2018/10/22 Javascript
JS事件循环机制event loop宏任务微任务原理解析
2020/08/04 Javascript
[42:20]Secret vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python中http请求方法库汇总
2016/01/06 Python
python绘制中国大陆人口热力图
2018/11/07 Python
selenium在执行phantomjs的API并获取执行结果的方法
2018/12/17 Python
十行代码使用Python写一个USB病毒
2019/06/21 Python
Python实用工具FuckIt.py介绍
2019/07/02 Python
python 实现百度网盘非会员上传超过500个文件的方法
2021/01/07 Python
常用的HTML5列表标签
2017/06/20 HTML / CSS
Linux Interview Questions For software testers
2013/05/17 面试题
2014年国培研修感言
2014/03/09 职场文书
质量月活动策划方案
2014/03/10 职场文书
《桥》教学反思
2014/04/09 职场文书
群众路线查摆问题整改措施
2014/10/10 职场文书
2014年英语教研组工作总结
2014/12/06 职场文书
2015年计算机教学工作总结
2015/07/22 职场文书
springboot临时文件存储目录配置方式
2021/07/01 Java/Android
JAVA SpringMVC实现自定义拦截器
2022/03/16 Python