FP-Growth算法实现

2023-12-04 10:50
文章标签 算法 实现 growth fp

本文主要是介绍FP-Growth算法实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

频繁项集挖掘(二)FP-Growth算法

FP-Growth(Frequent Patterns)相比于Apriori是一种更加有效的频繁项集挖掘算法,FP-Growth算法只需要对数据库进行两次扫描,而Apriori算法对于每次产生的候选项集都会扫描一次数据集来判断是否频繁,因此当数据量特别巨大,且扫描数据库的成本比较高时,FP-Growth的速度要比Apriori快。

但是FP-Growth只能用于发现频繁项集,不能用于发现关联规则。

FP-Growth原理分析

FP-Growth算法实现步骤

  • 构建FP树
  • 从FP树中挖掘频繁项集

FP-Growth算法将数据存储在一种被称为FP树的紧凑数据结构中。
在这里插入图片描述

下图就是利用上面的数据构建的一棵FP树(最小支持度为3):
在这里插入图片描述

  • FP树中最小支持度指项集总共出现的次数
  • 一个元素项可以在一棵FP树中出现多次
  • FP树存储项集的出现频率,且每个项集会以路径的方式存储在树中
  • 存在相似元素的集合会共享树的一部分
  • 只有当集合之间完全不同时,树才会分叉
  • 树节点上给出集合中的单个元素及其在序列中的出现次数,路径会给出该序列的出现次数

FP-Growth算法工作流程:

  • 扫描数据集两遍
  • 第一遍对所有元素项的出现次数进行计数
  • 根据前面的结论,如果某元素是不频繁的,那么包含该元素的超集也是不频繁的
  • 第二遍扫描,只考虑那些频繁元素,并且第二遍扫描开始构建FP树
算法实现
class treeNode(object):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 += numOccurdef 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):  # create FP-tree from dataset but don't mine'''遍历数据集两遍'''# 第一遍对元素计数originHeaderTable = {}    # headerTable用于记录树的结构情况for trans in dataSet:for item in trans:originHeaderTable[item] = originHeaderTable.get(item, 0) + dataSet[trans]popKeys = []# 过滤掉非频繁项集for k in originHeaderTable.keys():# 记录非频繁项if originHeaderTable[k] < minSup:popKeys.append(k)freqItemSet = set(originHeaderTable.keys()) - set(popKeys)# headerTable用于记录树的结构情况headerTable = {}if len(freqItemSet) == 0:   # 如果初选没有频繁项集,那么直接退出return None, None# 重新构建headerTablefor k in freqItemSet:headerTable[k] = [originHeaderTable[k], None]  # reformat headerTable to use Node linkdel originHeaderTable# 构建空树,根节点为空集root_node = treeNode('Null Set', 1, None)# 第二遍扫描,开始构建FP树for tranSet, count in dataSet.items():  # go through dataset 2nd timelocalD = {}for item in tranSet:  # put transaction items in orderif item in freqItemSet:localD[item] = headerTable[item][0]if len(localD) > 0:orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]updateTree(orderedItems, root_node, headerTable, count)  # populate tree with ordered freq itemsetreturn root_node, headerTable  # return tree and header tabledef updateTree(items, parentNode, headerTable, count):# 判断第一个项集是已经是当前节点的子节点if items[0] in parentNode.children:  # check if orderedItems[0] in retTree.children# 如果是,那么直接count + 1parentNode.children[items[0]].inc(count)  # incrament countelse:  # add items[0] to inTree.children# 如果不是,那么新建节点,并存储为当前节点的子节点parentNode.children[items[0]] = treeNode(items[0], count, parentNode)# 更新headerTable# 判断当前item是否是第一次记录if headerTable[items[0]][1] == None:# 如果是第一次,那么把新建的节点直接记录到头表中headerTable[items[0]][1] = parentNode.children[items[0]]else:# 如果不是第一次,那么说明新节点是当前item的节点的子节点,因此将它记录到当前分支的末位去,即设置为当前分支的叶子节点updateHeader(headerTable[items[0]][1], parentNode.children[items[0]])# 如果还有第二个元素,那么递归执行以上操作if len(items) > 1:updateTree(items[1::], parentNode.children[items[0]], headerTable, count)def updateHeader(lastNode, newLeafNode):# 判断上一节点是否有连接节点,如果没有,那么说明上一节点就是叶子节点,那么直接将新节点设为叶子节点while (lastNode.nodeLink != None):# 如果上一节点已经有连接节点,那么循环知道遍历到叶子节点,再设置新叶子节点lastNode = lastNode.nodeLink# 将新的叶子节点设置为旧叶子节点的连接节点lastNode.nodeLink = newLeafNodedef loadTestDataset():dataset = [['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 datasetdef createInitDataset(dataSet):dictDataset = {}for trans in dataSet:dictDataset[frozenset(trans)] = 1return dictDatasetdef buildCombinedItems(leafNode, combinedItems):if leafNode.parent != None:combinedItems.append(leafNode.name)buildCombinedItems(leafNode.parent, combinedItems)def buildCombinedDataset(nodeObject):# 根据节点名称,组合出新的项集节点combinedDataset = {}while nodeObject != None:combinedItems = []buildCombinedItems(nodeObject, combinedItems)if len(combinedItems) > 1:combinedDataset[frozenset(combinedItems[1:])] = nodeObject.countnodeObject = nodeObject.nodeLinkreturn combinedDatasetdef scanFPTree(headerTable, minSup, parentNodeNames, freqItemList):# 遍历排序后的headerTable,(节点名称,节点信息)for baseNode, nodeInfo in headerTable.items():# 根据prefixnewFreqSet = parentNodeNames.copy()newFreqSet.add(baseNode)# 节点计数值nodeCount = nodeInfo[0]# 节点对象nodeObject = nodeInfo[1]# 记录下频繁项集以及计数freqItemList.append((newFreqSet, nodeCount))# 根据当前节点的子节点,构建出新的项集组合combinedDataset = buildCombinedDataset(nodeObject)# 根据新的项集组合,重合构建子FP树subFPTree, subFPTreeHeaderTable = createTree(combinedDataset, minSup)# 如果头表不为空,那么递归新树的头表if subFPTreeHeaderTable != None:print('conditional tree for: ', newFreqSet)subFPTree.disp(1)# 根据新的头表 扫描FP-TreescanFPTree(subFPTreeHeaderTable, minSup, newFreqSet, freqItemList)if __name__ == '__main__':from pprint import pprintsimpDat = loadTestDataset()initSet = createInitDataset(simpDat)# 构建初始的FP-TreeinitFPtree, initFPtreeHeaderTable = createTree(initSet, 3)initFPtree.disp(1)freqItems = []    # 存储频繁项集# 扫描FP树,找出所有符合条件的频繁项集root_node_names = set([])    # 从根路径空集开始扫描scanFPTree(initFPtreeHeaderTable, 3, root_node_names, freqItems)pprint(freqItems)

freqItems = [] # 存储频繁项集
# 扫描FP树,找出所有符合条件的频繁项集

root_node_names = set([])    # 从根路径空集开始扫描
scanFPTree(initFPtreeHeaderTable, 3, root_node_names, freqItems)
pprint(freqItems)

这篇关于FP-Growth算法实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/453199

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

MySQL双主搭建+keepalived高可用的实现

《MySQL双主搭建+keepalived高可用的实现》本文主要介绍了MySQL双主搭建+keepalived高可用的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、测试环境准备二、主从搭建1.创建复制用户2.创建复制关系3.开启复制,确认复制是否成功4.同

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、