前言

FP-growth是一个非常好的频繁项集发现算法,广泛应用于搜索引擎中(找出经常在一起出现的词对)相对于Apriori算法,FP-growth算法只用对数据库进行两次扫描,因此更适用于处理大数据。

本文以代码为主。关于FP-growth的理解,请参照这个网址:https://www.cnblogs.com/pinard/p/6307064.html
我自认是没有这个能力写的这么好的哈哈。

FP-growth算法主要分成两部分:FP树的构建从FP树中挖掘频繁项集。本文主要介绍FP树的构建。

FP树的构建

这里,就假设大家已经了解了FP树是什么玩意了。
那么,就先从创建结点开始吧!

FP树结点

仔细观察上图,发现FP树的结点包括了以下信息:

  • name:结点的名字。比如图中的null, A等
  • count: 名字对应的计数值
  • nodelink:同名字结点之间的link。比如图中的虚线箭头
  • parent:父结点
  • children:子节点

另外,还需要两个方法:

  • inc方法:用来更新该结点的计数值(在向树中插入事务时)
  • disp方法:将树以文本形式显示(对调试很有用)

话不多说,直接上代码~

class treeNode:def __init__(self, nameValue, numOccur, parentNode):self.name = nameValueself.count = numOccurself.nodeLink = Noneself.parent = parentNodeself.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)

构建FP树

前文说过,FP-growth算法一共需要遍历两次数据集,那么究竟是哪两次呢

第一步:遍历数据集,获得每个元素项的出现频率,并去掉不满足最小支持度的元素项,生成头指针表。打个比方,假设有以下事务:

给定最小支持度为3,则生成的头指针表为:

你问还有一些元素项去哪了?它们都因为出现频率小于最小支持度而被删除了。

第二步:遍历数据集,根据头指针表,把数据集中的每一条事务中的元素按照其出现频率重新排序,并且删去没有出现在头指针表中的元素。再举个比方:

以事务1为例,首先元素项h, j, p未出现在头指针表中(也就是出现频率小于最小支持度),故被删除。而在头指针表中z排在r前面,因此处理后的事务1为z, r

第三步,就是将处理后的事务,挨个插入FP树中啦。

接下来就是代码部分。我不想像书上那样,把代码先放上来,再做阐释。我会先解释一下每个方法的作用,再给出代码,这样或许能好理解一点~不想看啰里啰唆的可以直接划到下面看代码哈

FP构建函数一共有三个方法:

方法1:createTree方法

createTree方法有以下几个功能

  • 遍历数据集,构建头指针表headerTable和频繁项集freqItemSet
  • 遍历数据集,根据头指针表对原事务数据集进行过滤和重排,对处理后的事务oderedItems, 调用updateTree方法来更新树。

代码如下:

# 传入的数据集其实是个字典,key值为事务, value值为数据集中该事务的频次
def createTree(dataSet, minSup=3):          headerTable = {}for trans in dataSet:for item in trans:headerTable[item] = headerTable.get(item, 0) + dataSet[trans]# 遍历数据集,构建headerTabel。其key为元素,value为元素出现频次for k in list(headerTable.keys()):# 如果元素频次小于最小支持度,则删去if headerTable[k] < minSup:del headerTable[k]              freqItemSet = set(headerTable.keys())if len(freqItemSet) == 0:return None, Nonefor k in headerTable:# 拓展头指针表,第二项存储着指向该元素在树中第一次出现位置的指针headerTable[k] = [headerTable[k], None]# 建一棵空树retTree = treeNode('Null Set', 1, None)       for tranSet, count in dataSet.items():# localD存储过滤后的数据集localD = {}                          for item in tranSet:if item in freqItemSet:localD[item] = headerTable[item][0]if len(localD) > 0:# orderedItems存储过滤并重排序后的数据集orderedItems = [v[0] for v in sorted(localD.items(),key=lambda p: p[1], reverse=True)]  updateTree(orderedItems, retTree,headerTable, count)   return retTree, headerTable

方法2:updateTree方法

(该部分最好一边画FP树再一边看)
updateTree是一个不断迭代的方法,它对orderedItem列表中第一个元素进行处理,最后删去该元素,然后再对新的orderedItem调用updateTree方法。

该函数首先测试第一个元素是否是作为子节点存在(从这里就看出为什么要对事务进行重排序了)。若是存在,则更新该元素的计数;若不存在,则创建一个treeNode并将其作为子节点添加到树中。

之后,头指针表也会更新,调用updateHeader方法。可能你会问为什么要更新头指针表?这个问题在updateHeader方法里面再解释哈。

def updateTree(items, inTree, headerTable, count):# 如果第一个元素项作为子节点存在,则更新计数值if items[0] in inTree.children:                                  inTree.children[items[0]].inc(count)else:# 否则创建一个新的子节点inTree.children[items[0]] = treeNode(items[0], count, inTree)# 如果在头指针表里该元素还没有指向的指针(即树上还没有出现该元素)if headerTable[items[0]][1] is None:                        headerTable[items[0]][1] = inTree.children[items[0]]else:updateHeader(headerTable[items[0]][1],inTree.children[items[0]])# 如果事务中不止一个元素,则去掉第一个元素,再迭代if len(items) > 1:                                              updateTree(items[1::], inTree.children[items[0]],headerTable, count)

方法3:updateHeader方法

前面说过,头指针表headTable里面不仅存储着频繁元素,还存储着指向树中第一次出现该元素的指针。
同时,大家如果仔细观察FP树的话,会发现树中同一个元素会有虚线相连,结点类中也有对应的属性,叫nodelink
所以,updateHeader方法的作用就是:

对于结点inTree.children[items[0]]和其对应元素, 此时在头指针表里该元素有指针存在(即树中已经有了该元素的存在),把这个元素所在结点和该元素上一次出现的结点“连”起来
至于怎么找到该元素上一次出现的结点嘛~

代码如下:

def updateHeader(nodeToTeset, targetNode):while nodeToTeset.nodeLink is not None:nodeToTeset = nodeToTeset.nodeLinknodeToTeset.nodeLink = targetNode

简单的数据集

树的构建已经大功告成了,接下来我们只要创建一个数据集就可了。
注意,我们要导入的不是事务的列表,而是一个键为事务,键值为事务出现频次的数据字典
因此我们需要对原始数据集进行一番处理
代码如下:

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 simpDatdef createInitSet(dataSet):retDict = {}for trans in dataSet:retDict[frozenset(trans)] = 1return retDict

完整代码

下面贴出完整代码:

class treeNode:def __init__(self, nameValue, numOccur, parentNode):self.name = nameValueself.count = numOccurself.nodeLink = Noneself.parent = parentNodeself.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)# 传入的数据集其实是个字典,key值为事务, value值为数据集中该事务的频次
def createTree(dataSet, minSup=3):headerTable = {}for trans in dataSet:for item in trans:headerTable[item] = headerTable.get(item, 0) + dataSet[trans]# 遍历数据集,构建headerTabel。其key为元素,value为元素出现频次for k in list(headerTable.keys()):# 如果元素频次小于最小支持度,则删去if headerTable[k] < minSup:del headerTable[k]freqItemSet = set(headerTable.keys())if len(freqItemSet) == 0:return None, Nonefor k in headerTable:# 拓展头指针表,第二项存储着指向该元素在树中第一次出现位置的指针headerTable[k] = [headerTable[k], None]# 建一棵空树retTree = treeNode('Null Set', 1, None)for tranSet, count in dataSet.items():# localD存储过滤后的数据集localD = {}for item in tranSet:if item in freqItemSet:localD[item] = headerTable[item][0]if len(localD) > 0:# orderedItems存储过滤并重排序后的数据集orderedItems = [v[0] for v in sorted(localD.items(),key=lambda p: p[1], reverse=True)]updateTree(orderedItems, retTree,headerTable, count)return retTree, headerTabledef updateTree(items, inTree, headerTable, count):# 如果第一个元素项作为子节点存在,则更新计数值if items[0] in inTree.children:inTree.children[items[0]].inc(count)else:# 否则创建一个新的子节点inTree.children[items[0]] = treeNode(items[0], count, inTree)# 如果在头指针表里该元素还没有指向的指针(即树上还没有出现该元素)if headerTable[items[0]][1] is None:headerTable[items[0]][1] = inTree.children[items[0]]else:updateHeader(headerTable[items[0]][1],inTree.children[items[0]])# 如果事务中不止一个元素,则去掉第一个元素,再迭代if len(items) > 1:updateTree(items[1::], inTree.children[items[0]],headerTable, count)def updateHeader(nodeToTeset, targetNode):while nodeToTeset.nodeLink is not None:nodeToTeset = nodeToTeset.nodeLinknodeToTeset.nodeLink = targetNodedef 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 simpDatdef createInitSet(dataSet):retDict = {}for trans in dataSet:retDict[frozenset(trans)] = 1return retDictsimpDat = loadSimpDat()
initSet = createInitSet(simpDat)
myFPtree, myHeaderTab = createTree(initSet, 3)
myFPtree.disp()

生成的结果如下:

Null Set 1z 5r 1x 3s 2t 2y 2r 1t 1y 1x 1s 1r 1

其中每个缩进表示所处的树的深度

FP树的构建到此结束。至于怎么从树中挖掘频繁项集,请听下回分解~

本文的代码来自《机器学习实战》的第12章

FP-growth:FP树的构建相关推荐

  1. 基于R的FP树fp growth 关联数据挖掘技术在煤矿隐患管理

    研究煤矿隐患数据的挖掘以实现海量隐患数据的有效利用,在分析矿山数据挖掘枝术和煤矿隐患数据特点的基础上,提出煤矿隐患数据挖掘是矿山数字化的重要组成部分,给出煤矿隐患数据挖掘的概念,设计了煤矿隐患数据挖掘 ...

  2. Frequent Pattern 挖掘之二(FP Growth算法)(转)

    FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法的代价,他不需要不断得生成候选项目队列和不断得扫描整个数据库进行比对.为了达到这样的效果,它采用了一种简洁的数据结 ...

  3. FP Growth算法

    转载自:http://blog.sina.com.cn/s/blog_68ffc7a40100uebg.html FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法 ...

  4. FP Growth算法详解

    看了n多资料,就这篇说的比较详细,适合初学者 FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法的代价,他不需要不断得生成候选项目队列和不断得扫描整个数据库进行比对 ...

  5. MapReduce框架下的FP Growth算法详解

    转载自:http://blog.sina.com.cn/s/blog_68ffc7a40100uebk.html Sharding 这一步没什么好讲的,将数据库分成连续的大小相等的几个块,放置在不同的 ...

  6. MapReduce框架下的FP Growth算法概述

    转载自:http://blog.sina.com.cn/s/blog_68ffc7a40100uebi.html 前面的博客分析了关联分析中非常重要的一个算法-FP Growth.该算法根据数据库在内 ...

  7. cJSON使用教程(树外构建 out of tree build 概念)(组包概念)

    JSON基础:包括组包的概念等 Github:DaveGamble/cJSON https://github.com/DaveGamble/cJSON 文章目录 Github:DaveGamble/c ...

  8. 【算法学习笔记】哈夫曼树的构建和哈夫曼编码的实现代码

    介绍 哈夫曼(Haffman)这种方法的基本思想如下: ①由给定的n个权值{W1,W2,-,Wn}构造n棵只有一个叶子结点的二叉树,从而得到一个二叉树的集合F={T1,T2,-,Tn}. ②在F中选取 ...

  9. MEGA | 多序列比对及系统发育树的构建

    MEGA是一个用于多序列比对和可视化.以及构建系统发育树的免费程序.自1993年发布以来,MEGA共更新9个版本 (没有第八.九版),今年发布的MEGA 11为处理更大的数据集进行了优化. 之前我们介 ...

最新文章

  1. 21Iterator(迭代器)模式
  2. Talend open studio数据导入、导出、同步Mysql、oracle、sqlserver简单案例
  3. poj 1703(种类并查集)
  4. SQL Server 2008 批量插入数据时报错
  5. 八十一、使用Springboot实现发邮件的需求
  6. shell脚本采用crontab定时备份数据库日志
  7. Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法笔记-协议与分类...
  8. F#基础教程 unit类型
  9. 为什么选择使用Sass而不是Less?
  10. cad注释比例和打印比例不一样_CAD中输出不同比例图纸效率不高,原因在这里
  11. python视频转图片
  12. git 分支关系图谱讲解
  13. Functional JavaScript: 使用 Transducer 提升函数式性能
  14. J.U.C之AQS:CLH同步队列
  15. MySQL的地理位置类型
  16. 魔方复原(BFS+剪枝)
  17. “道”与“术”之关系
  18. 在Java中中文需要用双引号吗_Java语言支持Unicode,自然支持中文,所以Java中可以使用中文的双引号、单引号、分号代替英文的双引号、单引号、分号。...
  19. AngularJS中文入门教程
  20. Halide 配置 visual studio

热门文章

  1. 快速定量,Abbkine 蛋白质定量试剂盒BCA法来了!
  2. Java中AudioFileStream_iOS音频学习一之AudioFileStream
  3. 凯文凯利著作《必然》
  4. java计算机毕业设计基于ssm的志愿者招募网站(源代码+数据库+Lw文档)
  5. 超强大的dnsmasq及hosts全自动脚本扶墙去广告详细教程
  6. 论文阅读笔记:Multi-model Databases: A New Journey to Handle the Variety of Data
  7. 读书笔记-写给所有人的逻辑思维课
  8. OpenCV 32F 与 8U Mat数据类型相互转换(C++版)
  9. bugkuCTF——猫片(安恒)
  10. Win7下基于Anaconda安装TensorFlow