关联规则挖掘
在上两篇文章中提到了关联规则基本原理理解(6(上))以及Apriori 算法挖掘原理介绍(6(中))。我们继续介绍另外一个算法。
FP-growth 算法
基本定义:
定义1 FP-tree
频繁模式树FP-tree是一个树形结构。包括一个频繁项组成的头表,一个标记为“null”的根节点,它的子节点为一个项前缀子树的集合。
定义2 频繁项
单个项目的支持度超过最小支持度则称其为频繁项(Frequent Item)。
定义3 频繁项头表
频繁项头表(Head Table)的每个表项由两个域组成:项目名称item-name和指针node_link。node_link指向FP-tree中具有与该表项相同item-name的第一个节点。
定义4.13 频繁项头表
每个项前缀子树(Item Prefix Subtree)的节点有三个域:item-name,count,node_link。item-name记录了该节点所代表的项的名字。count记录了所在路径代表的交易中包含此节点项目的交易个数。node_link指向下一个具有同样的item-name域的节点,要是没有这样一个节点,就为null。
这些定义相对枯燥,举个栗子
左边为频繁项头表,指针指向右边对应的节点,右边为PF-tree,根节点为空,由根节点往下一条路径上的节点表示一起在交易中出现。例如:路径{m,b}的计数5即为{m,b}项集在交易中出现的次数为5,由计数最小的b决定;路径{m,b,t}计数为3;项集{m,b,c}计数为3,为什么呢?
来看具体的过程。
算法过程
用 FP-增长(Frequent-Pattern Growth,FP-Growth)算法来发现频繁项集。算法只需扫描事务数据库两次,其计算过程主要由以下两步构成。
(1)构造FP-树
将事务数据库压缩到一棵频繁模式树(Frequent-Pattern Tree,简记为FP-树)之中,并让该树保留每个项的支持数和关联信息。
(2)生成频繁项集
由FP-树逐步生成关于项集的条件树,并根据条件树生成频繁项集。
例子:
交易数据集D为:最小支持数为3.
第一步 删去非频繁项
扫描数据集,计算每个项目的计数值,并保存在频繁项的集合F中,选出F中支持度大于3的项,并按照计数递减排序,将结果放入列表L中。
列表L
item | count | item | count |
milk | 8 | bread | 7 |
tea | 7 | cream | 3 |
我们之前说过,非频繁集的超集为非频繁集。
所以将交易数据集D中删去非频繁的项,并按照列表L中的顺序进行排序,更新后的交易数据集为:
第二步 产生FP-tree
创建一个标记为null的根结点。开始对D的第二遍扫描。
对第一个交易T,的扫描将建立这棵树的第一个路径: < milk:1,bread:1,tea:1,cream:1 >.
对于T2来说,它同已经存在的路径< milk:1,bread:1,tea:1,cream:1 >有共同的前缀< milk, bread>,所以把这个前缀中的所有结点的count 增加1。然后新结点(cream:1)被创建并且被作为结点(bread:2)的子结点。对T3,因为它的频繁项列表只与以milk为前缀的子树有一个共同结点(milk),所以把这个结点的count增加1。以此类推,扫描完整个数据库。
为了方便对树的遍历,建立一一个频繁项头表,头表表项的node_ link 指向树里面具有相同item_ name的结点。具有相同item_ name 的结点通过node_ link 被连接在一起,如图所示。
目前为止
可见,FP-树是事务数据库的一种压缩表示方法。
它通过逐个读入事务,并把每个事务映射为FP-树中的一条路径,且路径中的每个结点对应该事务中的一个项。
不同的事务如果有若干个相同的项,则它们在FP-树中用重叠的路径表示,用结点旁的数字标明该项的重复次数,作为项的支持数。
因此,路径相互重叠越多,使用FP-树结构表示事务数据库的压缩效果就越好。
如果FP-树足够小且能够在内存中存储,则可以从这个内存的树结构中直接提取频繁项集,而不必再重复地扫描存放在硬盘上的事务数据库。
第三步 产生条件子树,频繁集
得到频繁树后如何得到频繁集。这里有一个很巧妙的方法,因为树上一条路径表示他们在交易中一起出现,那么出现的次数由路径的末端决定(末端的个数较小,木桶效应),那么我们将包含某项频繁集挖掘出来的方法为:这项作为树叶它的所有祖先的集合组成的的子树挖掘出来,计数由末端决定。
既然包含某项的频繁集需要将其看做树叶,那么不是一般性,直接从树叶开始:
例子1:考虑项c的频繁子树
项c作为叶子节点,那么抽取出来的路径子树如上,左边的c出现的次数是2,那么对应上方的出现也为2(由叶子节点决定:因为要和叶子节点一起出现(考虑的就是包含c的频繁集));再往上的节点b的计数为3(2+1),因为和右边的路径重合且计数为1;再往上的m节点也是如此。
那么在这个频繁子树中,可以直接得到包含c的频繁集为:
{c}:3
{c,b}:3, {c,m}:3
{c,b,m}:3
为什么没有t?
因为t 和c 这条路径计数为2(只有在一条路径上才是一起在交易中出现)
例子2:考虑项t的频繁子树
那么在这个频繁子树中,可以直接得到包含t的频繁集为:
{t}:5
{t,b}:5, {t,m}:5
{t,b,m}:3(就是最左边那条路径,同时包含t,b,m,计数由最少的t决定)
例子3:考虑项b的频繁子树
那么在这个频繁子树中,可以直接得到包含b的频繁集为:
{b}:7
{b,m}:5, (就是最左边那条路径,同时包含b,m,计数由最少的b决定)
例子3:考虑项m的频繁子树
那么在这个频繁子树中,可以直接得到包含b的频繁集为:
{m}:8
综上所述即可得到所以的频繁集。
复盘基本流程:
第一部分:根据一个输入交易记录集建立一棵FP-tree。
输 入:交易记录集D,最小支持度supmin。
输 出:FP-tree。
(1)扫描数据库D一遍,得到频繁项的集合F和每个频繁项的支持度。把F按支持度递降排序,结果记为L。
(2)创建FP-tree的根节点,记为T,并且标记为“null”。然后对DB中的每个交易做如下的步骤:根据L中的顺序,选出并排序Trans中的频繁项。把Trans中排好序的频繁项列表记为[p|P],其中p是第一个元素,P是列表的剩余部分。调用insert_tree([p|P],T)。
函数insert_tree([p|P],T)的运行如下。
如果T有一个子结点N,其中N.item-name=p.item-name,则将N的count域值加1;
否则,创建一个新节点N,使它的count为1,使它的父节点为T,并且使它的node_link和那些具有相同item_name的域串起来。
如果P非空,则递归调用insert_tree(P,N)。
第二部分
输入: FP-tree。
输出:所有的频繁集。
FP-growth( Tree,a)
{
if Tree只有一-条路径P
then 对P中的结点的每一个组合(记为β)做(1)
①产生频繁集βUa,并且把它的支持度指定为β中结点的最小支持度,else
Tree的头表从表尾到表头的每一个表项(记为a)做(2)~(5)
②产生频繁集β=aUa,支持度为a的支持度
③建立关于β的FP-tree
④if关于β的FP-tree !=∅
⑤then调用FP-growth( Tree,p)
总结
# 算法的基础理论是:非频繁集的超集也是非频繁集,所以第一步就删除了所有的非频繁1-项集;
#更新交易数据库:删去非频繁项,并将每个交易按照项在交易中出现的次数从大到小排序;
#把每一条交易写入FP-tree中,若有相同的路径则不需要新建路径,直接对应节点计数+1,否则延伸新后代;
#频繁子树的挖掘:考虑包含某个项的子树,将其作为叶子节点,往上溯源它的祖先得到子树,节点计数以叶子节点为主,若节点有多个儿子,则为儿子的计数和;
#此算法只需要扫描两次交易数据集,无需生成大量候选集。
博文推荐
参考
陈志泊 主编. 数据仓库与数据挖掘(第二版). 清华大学出版社,2019