前言:
上面Apiori 频繁集里面,在训练的时候,每次调用ScanD,就需要遍历整个数据集。
如果数据集特别庞大,训练的速度会很慢。
FP-growth 是一种速度更快的发现频繁集的算法,完成相同的任务采用不同的算法。
应用: 挖掘常用词,从另一个网民网页浏览行为挖掘常见模式
FP:(Frequent Pattern): 频繁模式
目录
- 简介
- FP树构建
- 频繁项挖掘
- 代码实现
一 简介
Fp-growth 只会扫描数据集两次,发现频繁集流程如下:
1.1 构建FP 树
1.2 抽取条件模式基
1.3 根据条件模式基挖掘频繁项
一个元素可以在FP中多次出现,FP树会存储项集的出现频率,
每个项集回忆路径方式存储在树中
相似项之间的连接(同一个频繁项): 节点链接,用于快速发现相似项
二 FP树构建
2.1 第一遍 对所有的元素项出现的频率,去掉不满足最小支持度的选项。
原理同Aprior
事务ID | 事件元素 | 过滤后的事务 |
001 | r,z,h,j,p | z,r |
002 | z,y,x,w,v,u,t,s | z,x,y.s,t |
003 | z | z |
004 | r,x,n,o,s | x,s,r |
005 | y,r,x,z,q,t,p | z,x,y.r,t |
006 | y,z,x,e,q,s,t,m | z,x,y,s,t |
第二遍 只考虑过滤后的频繁集元素
从空集开始,不断添加频繁集选项,如果树中已经存在现有元素,则增加现有元素的值
如果元素不存在,则向树添加一个分支。
例子:
第一次遍历: 生成headerTable以及retTree
headerTable: 生成头指针表,【count, 指针链表】
freqSet | Count | 指针链表 |
---|---|---|
r | 3 | None |
z | 5 | None |
t | 3 | None |
y | 3 | None |
s | 3 | None |
x | 4 | None |
retTree树: 一颗空树
第二次遍历
1 遍历数据项: ['r','z','h','j','p']
获取频繁集 localD {'r': 3, 'z': 5}
支持度排序 curItems ['z', 'r']
生成一个按照当前分支生成一个tree,并且更新头指针表里面的指针headerTable
2 遍历数据项 ['z','x','y','w','v','u','t','s'],
频繁集: localD {'z': 5, 't': 3, 'y': 3, 's': 3, 'x': 4}
按支持度排序
curItems ['z', 'x', 't', 'y', 's']
3 数据集 ['z']
频繁度 localD {'z': 5}
支持度排序 curItems ['z']
4: 数据集 ['r','x','n','o','s']
频繁集: localD {'r': 3, 's': 3, 'x': 4}
排序: curItems ['x', 'r', 's']
5: 数据集['y','r','x','z','q','t','p'],
localD {'r': 3, 'z': 5, 't': 3, 'y': 3, 'x': 4}
curItems ['z', 'x', 'r', 't', 'y']
6 数据集 ['y','z','x','e','q','s','t','m']]
localD {'z': 5, 't': 3, 'y': 3, 's': 3, 'x': 4}
curItems ['z', 'x', 't', 'y', 's']
三: 频繁项挖掘
主要分为3个步骤:
3.1 从FP树中获得条件模式基
3.2 利用条件模式基作为数据集,构建一个条件FP树
3.3 迭代重复步骤1 步骤2 ,直到树包含一个元素项为止
A 抽取条件模式基:
从头指针表中的单个频繁项元素开始
获取对应的条件模式基: 以查找元素项为结尾的路径集合。每个路径都是一条前缀路径(prefix path)
一条前缀路径是介于所查找元素项与树根节点之间的所有内容[路径是前面nodeLink 头指针表保存的路径]
头指针表中频繁项 | 前缀路径 |
---|---|
z | {}: 5 |
r | {x,s}1,{z,x,y}1,{z}1 |
x | {z}3,{}1 |
y | {z,x}3 |
s | {z,x,y}2,{x}1 |
t: 3 | {z,x,y,s}2,{z,x,y,r}1 |
B: 创建条件FP树
根据头指针表中,对应的条件模式基
分析:
step1 频繁项:r,其条件模式基如下:
由条件模式基创建的头指针列表如下
当minSup=3 时,此刻myHead is None ,freqItemList [{'r'},]
step2 频繁项t: 条件模式基
头指针表如下:
此刻频繁列表为: freqItemList [{'r'}, {'t'}], 因为头指针表不为空,递归建立MineTree
先递归到Z:
freqItemList 变成 [{'r'}, {'t'} ,{t,z}]
因为 Z的prePath is None ,所以继续下一个X
此刻频繁集为:
freqItemList [{'r'}, {'t'}, {'t', 'z'}, {'t', 'x'}]
递归preFix 为{'t', 'x'} 的MinTree
freqItemList [{'r'}, {'t'}, {'t', 'z'}, {'t', 'x'}, {'t', 'z', 'x'}]
依次不断的递归头指针表中的频繁项
四 代码实现:
# -*- coding: utf-8 -*-
"""
Created on Tue Dec 24 10:47:11 2019
@author: chengxf2
"""
import numpy as np
class treeNode():
def __init__(self, name, count, parentNode):
self.name = name
self.count = count
self.nodeLink = None
self.parent = parentNode
self.children ={}
"""
增加节点计数
Args
num: 当前的数量
return
None
"""
def Inc(self, num):
self.count +=num
"""
显示树
Args
Id: 标签
return
None
"""
def Disp(self, Id=1):
#print(" "*Id, self.name, ":",self.count)
for child in self.children.values():
child.Disp(Id+1)
"""
加载数据集
Args
None
return
dataSet: tran: 交易, count
"""
def LoadDataSet():
dataList = [['r','z','h','j','p'],
['z','x','y','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']]
dataSet ={}
for trans in dataList:
dataSet[frozenset(trans)]=1
return dataSet
"""
生成Tree
Args
dataSet: 生成数据集
minSup: 最小支持度
return
retTree: 树结构
headerTable: 头指针表
"""
def FpTree(dataSet, minSup):
headerTable = {}
#print("\n ************第1次遍历整个数据集*****************\n")
###获得支持度
for trans in dataSet:
for item in trans:
headerTable [item]= headerTable.get(item, 0)+dataSet[trans]
##去掉低支持度的选项
keys = list(headerTable.keys())
for key in keys:
if headerTable[key]<minSup :
headerTable.pop(key)
###获得频繁集
freqSet = set(headerTable.keys())
if 0 == len(freqSet):
return None , None
for key in headerTable : ##首先置空
headerTable[key] =[headerTable[key], None]
#print("\n headerTable",headerTable)
# print("\n ************第2次遍历整个数据集*****************\n")
retTree = treeNode("Root",1,None) ##创建树
for tranSet, count in dataSet.items(): ##第二次遍历整个数据集,开始默认是1
localD ={}
#print("\n tranSet ",tranSet)
for item in tranSet:
if item in freqSet: ##如果是频繁集
localD[item]=headerTable[item][0] ##返回分支 指针
# print("\n localD ",localD)
if len(localD)>0:
curItems = [v[0] for v in sorted(localD.items(), key = lambda p:p[1], reverse= True)] ##排序,从大到小
# print("\n curItems ",curItems)
updateTree(curItems, retTree, headerTable, count)#将排序后的item集合填充的树中
#print("\n ====================\n")
#retTree.Disp()
#print("\n headerTable",headerTable)
return retTree, headerTable
"""
FP生长
Args
items: 项集
inTree: 当前的Tree:
headerTable: 头指针表
count: 计数
"""
def updateTree(items, inTree, headerTable, count):
# print("items[0] ",items[0])
node = items[0]
if node in inTree.children: ##第一个元素是否是子节点,增加计数
inTree.children[items[0]].Inc(count)
else: ##创建一个新的节点,加入到 头指针表中
inTree.children[node]= treeNode(node, count, inTree) ##生成一个节点
if headerTable[node][1] == None: ##更新头指针
headerTable[node][1] = inTree.children[node]
else: ##
updateHeader(headerTable[node][1], inTree.children[node])
if len(items)>1: ##删掉表中第一个元素
updateTree(items[1::], inTree.children[node],headerTable, count)
"""
链表链接指向书中该元素的每一个实例
延着链表开始,一直到达链表结尾
Args
nodeLink:
targetNode:
"""
def updateHeader(node, targetNode):
while (not node.nodeLink is None):
node = node.nodeLink
node.nodeLink = targetNode
"""
向上回溯整个树到根节点
Args
leafNode:当前节点
path:当前的路径
return
None
"""
def AscendTree(leafNode, path):
if leafNode.parent is not None:
path.append(leafNode.name)
AscendTree(leafNode.parent, path)
"""
"""
def FindPrefixPath(key, headerTable):
treeNode = headerTable[key][1]
condParent ={}
while treeNode is not None:
path = []
AscendTree(treeNode, path)
if len(path)>1:
condParent[frozenset(path[1:])]= treeNode.count
treeNode = treeNode.nodeLink
return condParent
def MinTree( headerTable, minSup, preSet, freqItemList):
KeySort = [v[0] for v in sorted(headerTable.items(), key=lambda p:p[1][0])] # 根据频繁项的count 从小到大排序
#print("bigL ",bigL)
for key in KeySort:
newFreqSet = preSet.copy()
newFreqSet.add(key)
freqItemList.append(newFreqSet)
conBase = FindPrefixPath(key,headerTable) ##获取条件模式基
myCondTree, myHead = FpTree(conBase,minSup) ##把条件模式基当作样本,建立FP树
if myHead is not None:
MinTree( myHead, minSup, newFreqSet, freqItemList) ##递归挖掘FP树
def FP():
##先构建FP树
print("\n **********构建FP树结束****************\n")
dataSet =LoadDataSet()
retTree, headerTable =FpTree(dataSet,3)
"""
print("\n **********抽取条件模式基****************\n")
for key in headerTable:
condParent= FindPrefixPath(key ,headerTable)
print("\n key: ",key,"\t conParent: ",condParent)
print("\n **********创建条件FP树****************\n")
"""
freqItems =[]
MinTree(headerTable,3, set([]), freqItems)
for item in freqItems:
print("\n item: \t ",item)
FP()
参考文档:
《机器学习实战》