使用 FP-growth 算法高效挖掘海量数据中的频繁项集

前言

  对于如何发现一个数据集中的频繁项集,前文讲解的经典 Apriori 算法能够做到。

  然而,对于每个潜在的频繁项,它都要检索一遍数据集,这是比较低效的。在实际的大数据应用中,这么做就更不好了。

  本文将介绍一种专门检索频繁项集的新算法 - FP-growth 算法。

  它只会扫描数据集两次,能循序挖掘出频繁项集。因此这种算法在网页信息处理中占据着非常重要的地位。

FP-growth 算法基本原理

  将数据存储到一种成为 FP 树的数据结构中,这样的一棵树包含了数据集中满足最小支持度阈值的所有节点信息以及对应的支持度信息。

  后面会专门讲解如何实现这样的一棵树。

  构建好了 FP 树之后,通过一定规则遍历这棵树就能挖掘出频繁项集。

  后面也会专门讲解如何遍历 FP 树。

FP 树

  下图为 FP 树架构示意图:

  

  右边部分中,"z:5" 的意思是,z在这条路径上出现了5次(包含其单个出现,也包含组合出现的情况)。

  顺着 "z:5" 往下,看到的是 r=1。这表示在这条路径上 "zr" 出现了一次。

  每个节点在树中都不是唯一的,比如能够清楚看到 r 出现了好几次。

  左边的部分,存放的是所有原子项出现的个数(包含其单个出现,也包含组合出现的情况),同时它还 ”派生" 出一个链表将所有树结构部分中的相同原子项串起来。

  比如左边部分 r=3 就派生出一个链表串起了 3 个 r 节点,每个 r 节点标记都为1,故左边部分 r=3。

构建 FP 树

  FP 树的机构稍微有点复杂,因此就不用字典,转用类(对象)来存树了。

  下面是 FP 树结构的代码实现:

 1 class treeNode:
 2     ‘FP 树节点‘
 3
 4     #=====================================
 5     # 输入:
 6     #        nameValue: 节点名称
 7     #        numOccur: 节点路径出现次数
 8     #        parentNode: 父节点
 9     #=====================================
10     def __init__(self, nameValue, numOccur, parentNode):
11         ‘初始化函数‘
12
13         # 节点名字
14         self.name = nameValue
15         # 节点的路径出现次数
16         self.count = numOccur
17         # 指向相似节点
18         self.nodeLink = None
19         # 父节点
20         self.parent = parentNode
21         # 子节点
22         self.children = {}
23
24     #=====================================
25     # 输入:
26     #        numOccur: 节点路径出现次数增量
27     #=====================================
28     def inc(self, numOccur):
29         ‘增加节点出现次数‘
30
31         # 增加这个节点的路径出现次数
32         self.count += numOccur
33
34     #=====================================
35     # 输入:
36     #        ind: 节点路径出现次数增量
37     #        ind: 子树序号
38     #=====================================
39     def disp(self, ind=1):
40         ‘将树以文本形式显示‘
41
42         print ‘  ‘*ind, self.name, ‘ ‘, self.count
43         for child in self.children.values():
44             child.disp(ind+1)

  以上代码定义的基本的 FP 树节点数据结构。接下来就是树生成部分以及表头(见上一部分图中左部)生成部分代码:

  1 def loadSimpDat():
  2     ‘载入测试数据‘
  3
  4     simpDat = [[‘r‘, ‘z‘, ‘h‘, ‘j‘, ‘p‘],
  5                [‘z‘, ‘y‘, ‘x‘, ‘w‘, ‘v‘, ‘u‘, ‘t‘, ‘s‘],
  6                [‘z‘],
  7                [‘r‘, ‘x‘, ‘n‘, ‘o‘, ‘s‘],
  8                [‘y‘, ‘r‘, ‘x‘, ‘z‘, ‘q‘, ‘t‘, ‘p‘],
  9                [‘y‘, ‘z‘, ‘x‘, ‘e‘, ‘q‘, ‘s‘, ‘t‘, ‘m‘]]
 10     return simpDat
 11
 12 def createInitSet(dataSet):
 13     ‘将测试数据格式化为字典‘
 14
 15     retDict = {}
 16     for trans in dataSet:
 17         retDict[frozenset(trans)] = 0
 18     for trans in dataSet:
 19         retDict[frozenset(trans)] += 1
 20     return retDict
 21
 22 #=====================================
 23 # 输入:
 24 #        dataSet: 数据集
 25 #        minSup: 最小支持度(实际为次数)
 26 # 输出:
 27 #        retTree: FP 树结构
 28 #        headerTable: 表结构
 29 #=====================================
 30 def createTree(dataSet, minSup=1):
 31     ‘创建 FP 树及其对应表结构‘
 32
 33     # 连续两次遍历数据集。第一次获取所有数据项及个数;第二次会支持度过滤。
 34     # 单元素频繁集(含出现次数)
 35     headerTable = {}
 36     for trans in dataSet:
 37         for item in trans:
 38             headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
 39     for k in headerTable.keys():
 40         if headerTable[k] < minSup:
 41             del(headerTable[k])
 42
 43     # 单元素频繁集(不含次数)
 44     freqItemSet = set(headerTable.keys())
 45     # 没有合乎要求的数据项则退出
 46     if len(freqItemSet) == 0:
 47         return None, None
 48
 49     # 对表数据结构进行格式化,使之能够存放指针。
 50     for k in headerTable:
 51         headerTable[k] = [headerTable[k], None]
 52
 53     # 新建初始化树节点
 54     retTree = treeNode(‘Null Set‘, 1, None)
 55     for tranSet, count in dataSet.items():
 56
 57         # 当前事务的单元素集(含次数)
 58         localD = {}
 59         for item in tranSet:
 60             if item in freqItemSet:
 61                 localD[item] = headerTable[item][0]
 62
 63         if len(localD) > 0:
 64             # 对localD中所有元素进行排序
 65             orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]
 66             # 更新 FP 树
 67             updateTree(orderedItems, retTree, headerTable, count)
 68
 69     # 返回FP树和表头结构
 70     return retTree, headerTable
 71
 72 #=====================================
 73 # 输入:
 74 #        items: 事务项
 75 #        inTree: FP 树
 76 #        headerTable: 表结构
 77 #        count: 事务项的个数
 78 #=====================================
 79 def updateTree(items, inTree, headerTable, count):
 80     ‘FP 树生长函数‘
 81
 82     # 检查事务项中的第一个元素是否作为树的直接子节点存在
 83     if items[0] in inTree.children:
 84         # 存在则直接更新子树节点
 85         inTree.children[items[0]].inc(count)
 86     else:
 87         # 不存在则更新树结构
 88         inTree.children[items[0]] = treeNode(items[0], count, inTree)
 89         # 更新表结构
 90         if headerTable[items[0]][1] == None:
 91             headerTable[items[0]][1] = inTree.children[items[0]]
 92         else:
 93             updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
 94
 95     # 递归调用此树生长函数
 96     if len(items) > 1:
 97         updateTree(items[1::], inTree.children[items[0]], headerTable, count)
 98
 99 #=====================================
100 # 输入:
101 #        nodeToTest: 指定表结构中的成员
102 #        targetNode: 待加入链表节点的指针
103 #=====================================
104 def updateHeader(nodeToTest, targetNode):
105     ‘表结构更新函数‘
106
107     while (nodeToTest.nodeLink != None):
108         nodeToTest = nodeToTest.nodeLink
109
110     nodeToTest.nodeLink = targetNode
111
112 def main():
113     ‘FP 树构建与展示‘
114
115     # 载入测试数据
116     simpDat = loadSimpDat()
117     # 将测试数据格式化为字典
118     initSet = createInitSet(simpDat)
119     # 创建 FP 树及对应表结构
120     myFPTree, myHeaderTab = createTree(initSet, 3)
121     # 展示 FP 树
122     myFPTree.disp()

  测试结果:

  

从 FP 树中挖掘频繁项集

  FP 树构建好之后,就能对它进行挖掘,以找到所有频繁项集。

  挖掘过程仅仅针对 FP 树和它对应的表结构,与原数据集没有任何关系了。

  挖掘的步骤如下:

    1. 从 FP 树中获得条件模式基

    2. 利用条件模式基,获得一个条件 FP 树。

    3. 迭代 1,2 直到树只剩下一个元素项。

  下面分别讲解如何抽取条件模式基,以及如何从条件模式基构建 FP 树。

条件模式基的抽取

  条件模式是指以所查找元素项为结尾的路径。

  对于如下 FP 树来说:

  

  r 的条件模式基为 {z},{x,s},{z,x,y} 。

  而上图的左部表结构就是用来求取抽象基的一个辅助结构。通过每个子链表的每个节点都能向上迭代获取到一个路径,一个链表得到的所有路径和就是该链表元素项对应的条件模式基。

  如下部分代码可用来求取元素项的条件模式基:

 1 #=====================================
 2 # 输入:
 3 #        leafNode: 表结构的子链表的节点
 4 #        prefixPath: 待返回路径
 5 #=====================================
 6 def ascendTree(leafNode, prefixPath):
 7     ‘获取FP树某个叶子节点的前缀路径‘
 8
 9     if leafNode.parent != None:
10         prefixPath.append(leafNode.name)
11         ascendTree(leafNode.parent, prefixPath)
12
13 #=====================================
14 # 输入:
15 #        basePat: 元素项
16 #        treeNode: 某个链表指向某首叶子节点的指针
17 #=====================================
18 def findPrefixPath(basePat, treeNode):
19     ‘获取表结构中某个元素项的条件模式基‘
20
21     # 条件模式基
22     condPats = {}
23
24     # 获取某个元素项的条件模式基础并返回
25     while treeNode != None:
26         prefixPath = []
27         ascendTree(treeNode, prefixPath)
28         if len(prefixPath) > 1:
29             condPats[frozenset(prefixPath[1:])] = treeNode.count
30         treeNode = treeNode.nodeLink
31
32     return condPats

条件 FP 树的构建与频繁项集的挖掘

  对于每个频繁项,都要创建一个条件 FP 树。

  条件 FP 树的创建代码,和之前的 FP 树是一样的。不过输入数据集会变成第一次求出的条件模式。

  而且,第一次挖掘的仅仅是最小元素项,而后面挖掘出的频繁项都会基于上一步结果叠加。

  如此一直迭代下去直到表结构为空。

  实现代码如下:

 1 #=====================================
 2 # 输入:
 3 #        inTree: 条件FP树
 4 #        headerTable: 条件表头结构
 5 #        minSup: 最小支持度
 6 #        preFix: 上一轮的频繁项
 7 #        freqItemList: 频繁项集
 8 #=====================================
 9 def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
10     ‘挖掘频繁项集‘
11
12     # 对表结构进行重排序(由小到大)
13     bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])]
14
15     # 遍历表结构
16     for basePat in bigL:
17         # 生成新频繁子项
18         newFreqSet = preFix.copy()
19         newFreqSet.add(basePat)
20         # 加入频繁集
21         freqItemList.append(newFreqSet)
22         # 挖掘新的条件模式基
23         condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
24         # 建立新的条件FP树
25         myCondTree, myHead = createTree(condPattBases, minSup)
26         # 若表结构非空,递归挖掘。
27         if myHead != None:
28             mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)

  测试结果:

  

小结

  1. FP-growth 算法使用了新的数据结构,而且创建,遍历过程递归代码比较多,因此理解起来有点难度。

  2. FP-growth 算法一般用来用来挖掘频繁项集,不用来学习关联规则。

  3. 大数据领域中机器学习的部分就暂告一段落了(已学习完最为经典的算法)。接下来的精力将主要放在 Hadoop 云平台的使用及其底层机制实现部分。

  

时间: 2024-10-07 15:58:45

使用 FP-growth 算法高效挖掘海量数据中的频繁项集的相关文章

关联分析中寻找频繁项集的FP-growth方法

关联分析是数据挖掘中常用的分析方法.一个常见的需求比如说寻找出经常一起出现的项目集合. 引入一个定义,项集的支持度(support),是指所有包含这个项集的集合在所有数据集中出现的比例. 规定一个最小支持度,那么不小于这个最小支持度的项集称为频繁项集(frequent item set). 如何找到数据集中所有的频繁项集呢? 最简单的方法是对所有项集进行统计,可以通过逐渐增大项集大小的方式来遍历所有项集.比如说下面的数据集,先统计所有单个元素集合的支持度,{z} 的支持度为5 (这里把项目出现次

Spark下的FP-Growth和Apriori(频繁项集挖掘并行化算法)

频繁项集挖掘是一个关联式规则挖掘问题.关联挖掘是数据挖掘中研究最早也是最活跃的领域,其中频繁模式的挖掘是关联挖掘的核心和基础,是产生关联规则挖掘的基础.频繁项集最经典的应用就是超市的购物篮分析. 首先要理解频繁项集中的以下概念. 频繁项:在多个集合中,频繁出现的元素项. 频繁项集:在一系列集合中每项都含有某些相同的元素,这些元素形成一个子集,满足一定阀值就是频繁项集. K项集:K个频繁项组成的一个集合. 支持度:包含频繁项集(F)的集合的数目. 可信度:频繁项与某项的并集的支持度与频繁项集支持度

FP-Growth算法之频繁项集的挖掘(python)

前言: 关于 FP-Growth 算法介绍请见:FP-Growth算法的介绍. 本文主要介绍从 FP-tree 中提取频繁项集的算法.关于伪代码请查看上面的文章. FP-tree 的构造请见:FP-Growth算法之 FP-tree 的构造(python). 正文: tree_miner.py文件: #coding=utf-8 import tree_builder import copy class Tree_miner(object): """tree_miner类. 作

FP-Growth算法python实现之频繁项集的挖掘

本文主要介绍从FP-tree中提取频繁项集的算法. 更多请见:FP-Growth算法的介绍.FP_Growth算法python实现.FP-Growth算法python实现之 FP-tree的构造. tree_miner.py代码: #coding=utf-8 import tree_builder import copy class Tree_miner(object): """tree_miner类. 作用:对Tree进行频繁项集的挖掘"""

技术文章 | 频繁项集挖掘算法之FPGrowth

频繁项集挖掘算法用于挖掘经常一起出现的item集合(称为频繁项集),通过挖掘出这些频繁项集,当在一个事务中出现频繁项集的其中一个item,则可以把该频繁项集的其他item作为推荐. 比如经典的购物篮分析中啤酒.尿布故事,啤酒和尿布经常在用户的购物篮中一起出现,通过挖掘出啤酒.尿布这个啤酒项集,则当一个用户买了啤酒的时候可以为他推荐尿布,这样用户购买的可能性会比较大,从而达到组合营销的目的. 常见的频繁项集挖掘算法有两类,一类是Apriori算法,另一类是FPGrowth.Apriori通过不断的

FP-growth算法发现频繁项集(二)——发现频繁项集

上篇介绍了如何构建FP树,FP树的每条路径都满足最小支持度,我们需要做的是在一条路径上寻找到更多的关联关系. 抽取条件模式基 首先从FP树头指针表中的单个频繁元素项开始.对于每一个元素项,获得其对应的条件模式基(conditional pattern base),单个元素项的条件模式基也就是元素项的关键字.条件模式基是以所查找元素项为结尾的路径集合.每一条路径其实都是一条前辍路径(perfix path).简而言之,一条前缀路径是介于所査找元素项与树根节点之间的所有内容. 下图是以{s:2}或{

发现频繁项集的方法 Apriori算法

我们是通过算法来找到数据之间的关联规则(两个物品之间可能存在很强的相关关系)和频繁项集(经常出现在一起的物品的集合). 我们是通过支持度和置信度来定义关联规则和频繁项集的 一个项集支持度是指在所有数据集中出现这个项集的概率,项集可能只包含一个选项,也有可能是多个选项的组合. 置信度 针对于啤酒-->尿布这样的关联规则来定义,计算方式为支持度(啤酒,尿布)/支持度啤酒,其中支持度(啤酒.尿布)为3/5,支持度啤酒为4/5,所以他的置信度为3/4,即置信度为75%,这意味着在啤酒的规则中,有0.75

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

FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法的代价,他不需要不断得生成候选项目队列和不断得扫描整个数据库进行比对.为了达到这样的效果,它采用了一种简洁的数据结构,叫做frequent-pattern tree(频繁模式树).下面就详细谈谈如何构造这个树,举例是最好的方法.请看下面这个例子: 这张表描述了一张商品交易清单,abcdefg代表商品,(ordered)frequent items这一列是把商品按照降序重新进行了排列,这个排序很重要,我们操作的所

Aprior算法、FP Growth算法

数据挖掘中有一个很重要的应用,就是Frequent Pattern挖掘,翻译成中文就是频繁模式挖掘.这篇博客就想谈谈频繁模式挖掘相关的一些算法. 定义 何谓频繁模式挖掘呢?所谓频繁模式指的是在样本数据集中频繁出现的模式.举个例子,比如在超市的交易系统中,记载了很多次交易,每一次交易的信息包括用户购买的商品清单.如果超市主管是个有心人的话,他会发现尿不湿,啤酒这两样商品在许多用户的购物清单上都出现了,而且频率非常高.尿不湿,啤酒同时出现在一张购物单上就可以称之为一种频繁模式,这样的发掘就可以称之为