前言

  对于如何发现一个数据集中的频繁项集,前文讲解的经典 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 树结构的代码实现:

 class treeNode:
'FP 树节点' #=====================================
# 输入:
# nameValue: 节点名称
# numOccur: 节点路径出现次数
# parentNode: 父节点
#=====================================
def __init__(self, nameValue, numOccur, parentNode):
'初始化函数' # 节点名字
self.name = nameValue
# 节点的路径出现次数
self.count = numOccur
# 指向相似节点
self.nodeLink = None
# 父节点
self.parent = parentNode
# 子节点
self.children = {} #=====================================
# 输入:
# numOccur: 节点路径出现次数增量
#=====================================
def inc(self, numOccur):
'增加节点出现次数' # 增加这个节点的路径出现次数
self.count += numOccur #=====================================
# 输入:
# ind: 节点路径出现次数增量
# ind: 子树序号
#=====================================
def disp(self, ind=1):
'将树以文本形式显示' print ' '*ind, self.name, ' ', self.count
for child in self.children.values():
child.disp(ind+1)

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

 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:
retDict[frozenset(trans)] = 0
for trans in dataSet:
retDict[frozenset(trans)] += 1
return retDict #=====================================
# 输入:
# dataSet: 数据集
# minSup: 最小支持度(实际为次数)
# 输出:
# retTree: FP 树结构
# headerTable: 表结构
#=====================================
def createTree(dataSet, minSup=1):
'创建 FP 树及其对应表结构' # 连续两次遍历数据集。第一次获取所有数据项及个数;第二次会支持度过滤。
# 单元素频繁集(含出现次数)
headerTable = {}
for trans in dataSet:
for item in trans:
headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
for k in headerTable.keys():
if headerTable[k] < minSup:
del(headerTable[k]) # 单元素频繁集(不含次数)
freqItemSet = set(headerTable.keys())
# 没有合乎要求的数据项则退出
if len(freqItemSet) == 0:
return None, None # 对表数据结构进行格式化,使之能够存放指针。
for k in headerTable:
headerTable[k] = [headerTable[k], None] # 新建初始化树节点
retTree = treeNode('Null Set', 1, None)
for tranSet, count in dataSet.items(): # 当前事务的单元素集(含次数)
localD = {}
for item in tranSet:
if item in freqItemSet:
localD[item] = headerTable[item][0] if len(localD) > 0:
# 对localD中所有元素进行排序
orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]
# 更新 FP 树
updateTree(orderedItems, retTree, headerTable, count) # 返回FP树和表头结构
return retTree, headerTable #=====================================
# 输入:
# items: 事务项
# inTree: FP 树
# headerTable: 表结构
# count: 事务项的个数
#=====================================
def updateTree(items, inTree, headerTable, count):
'FP 树生长函数' # 检查事务项中的第一个元素是否作为树的直接子节点存在
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] == 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) #=====================================
# 输入:
# nodeToTest: 指定表结构中的成员
# targetNode: 待加入链表节点的指针
#=====================================
def updateHeader(nodeToTest, targetNode):
'表结构更新函数' while (nodeToTest.nodeLink != None):
nodeToTest = nodeToTest.nodeLink nodeToTest.nodeLink = targetNode def main():
'FP 树构建与展示' # 载入测试数据
simpDat = loadSimpDat()
# 将测试数据格式化为字典
initSet = createInitSet(simpDat)
# 创建 FP 树及对应表结构
myFPTree, myHeaderTab = createTree(initSet, 3)
# 展示 FP 树
myFPTree.disp()

  测试结果:

  

从 FP 树中挖掘频繁项集

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

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

  挖掘的步骤如下:

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

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

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

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

条件模式基的抽取

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

  对于如下 FP 树来说:

  

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

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

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

 #=====================================
# 输入:
# leafNode: 表结构的子链表的节点
# prefixPath: 待返回路径
#=====================================
def ascendTree(leafNode, prefixPath):
'获取FP树某个叶子节点的前缀路径' if leafNode.parent != None:
prefixPath.append(leafNode.name)
ascendTree(leafNode.parent, prefixPath) #=====================================
# 输入:
# basePat: 元素项
# treeNode: 某个链表指向某首叶子节点的指针
#=====================================
def findPrefixPath(basePat, treeNode):
'获取表结构中某个元素项的条件模式基' # 条件模式基
condPats = {} # 获取某个元素项的条件模式基础并返回
while treeNode != None:
prefixPath = []
ascendTree(treeNode, prefixPath)
if len(prefixPath) > 1:
condPats[frozenset(prefixPath[1:])] = treeNode.count
treeNode = treeNode.nodeLink return condPats

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

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

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

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

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

  实现代码如下:

 #=====================================
# 输入:
# inTree: 条件FP树
# headerTable: 条件表头结构
# minSup: 最小支持度
# preFix: 上一轮的频繁项
# freqItemList: 频繁项集
#=====================================
def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
'挖掘频繁项集' # 对表结构进行重排序(由小到大)
bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])] # 遍历表结构
for basePat in bigL:
# 生成新频繁子项
newFreqSet = preFix.copy()
newFreqSet.add(basePat)
# 加入频繁集
freqItemList.append(newFreqSet)
# 挖掘新的条件模式基
condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
# 建立新的条件FP树
myCondTree, myHead = createTree(condPattBases, minSup)
# 若表结构非空,递归挖掘。
if myHead != None:
mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)

  测试结果:

  

小结

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

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

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

  

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

  1. 第十五篇:使用 FP-growth 算法高效挖掘海量数据中的频繁项集

    前言 对于如何发现一个数据集中的频繁项集,前文讲解的经典 Apriori 算法能够做到. 然而,对于每个潜在的频繁项,它都要检索一遍数据集,这是比较低效的.在实际的大数据应用中,这么做就更不好了. 本 ...

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

    关联分析是数据挖掘中常用的分析方法.一个常见的需求比如说寻找出经常一起出现的项目集合. 引入一个定义,项集的支持度(support),是指所有包含这个项集的集合在所有数据集中出现的比例. 规定一个最小 ...

  3. 关联规则—频繁项集Apriori算法

    频繁模式和对应的关联或相关规则在一定程度上刻画了属性条件与类标号之间的有趣联系,因此将关联规则挖掘用于分类也会产生比较好的效果.关联规则就是在给定训练项集上频繁出现的项集与项集之间的一种紧密的联系.其 ...

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

    前言: 关于 FP-Growth 算法介绍请见:FP-Growth算法的介绍. 本文主要介绍从 FP-tree 中提取频繁项集的算法.关于伪代码请查看上面的文章. FP-tree 的构造请见:FP-G ...

  5. 海量数据挖掘MMDS week2: 频繁项集挖掘 Apriori算法的改进:非hash方法

    http://blog.csdn.net/pipisorry/article/details/48914067 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...

  6. 频繁项集挖掘之apriori和fp-growth

    Apriori和fp-growth是频繁项集(frequent itemset mining)挖掘中的两个经典算法,虽然都是十几年前的,但是理解这两个算法对数据挖掘和学习算法都有很大好处.在理解这两个 ...

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

    上篇介绍了如何构建FP树,FP树的每条路径都满足最小支持度,我们需要做的是在一条路径上寻找到更多的关联关系. 抽取条件模式基 首先从FP树头指针表中的单个频繁元素项开始.对于每一个元素项,获得其对应的 ...

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

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

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

    Frequent Pattern 挖掘之二(FP Growth算法) FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法的代价,他不需要不断得生成候选项目队列和不断 ...

随机推荐

  1. LINUX RHCE 笔记

    磁盘类: 新增一个SCSI磁盘,LINUX无法看到(不重启)echo "- - -" >  /sys/class/scsi_host/host2/scan就看到了. part ...

  2. virtual_login

    from selenium import webdriverimport timedriver = webdriver.Chrome()driver.set_window_position(30, 4 ...

  3. 不同版本jq冲突问题

    在网上找了几个qq客服的js代码,本地调试没问题一加到网站上就出现问题了各种不对.最后发现是jq的问题,网站中有不同的jq冲突了,解决方法: <script>var $j = jQuery ...

  4. 一个简单的金额平均分配函数(C#版)

    //总金额平均分配给总人数 //参数说明:总金额,总人数,最大金额为平均金额的倍率 public double[] GetList(double zje,int zrs,int max) { doub ...

  5. Unity3d之个性化鼠标

    代码实例: using UnityEngine; using System.Collections; public class CursorController : MonoBehaviour { / ...

  6. JAVAFX纯手写布局

    主页面效果: 第一栏的效果: 工程目录: package MessageBean; /** * * @author novo */ public class Message { private Str ...

  7. 作业七:团队项目——Alpha版本冲刺阶段005

    今日进展:编写代码. 今日安排:完成开始部分代码.

  8. dedecms qq咨询平均分配

    qq后台页: qq_admin.php <style type="text/css"> <!-- * {margin:0; padding:0;} .wrap { ...

  9. PHP JS JQ 异步上传并立即显示图片

    提交页面: <! DOCTYPE html> < html> < head> < meta charset ="GB2312" > ...

  10. php 读取网页源码 , 导出成txt文件, 读取xls,读取文件夹下的所有文件的文件名

    <?php // 读取网页源码$curl = curl_init();curl_setopt($curl, CURLOPT_URL, $url);curl_setopt($curl, CURLO ...