(一)综述:

概念介绍

频繁项集指的是频繁共同出现的 item组成的集合。如在购物场景下,用户常常同时购买 A 和 B 两种物品。A 和 B则构成一个频繁项集合。挖掘频繁项集能够帮助商家向用户推送商品,如在淘宝上购买完鼠标后往往会出现鼠标垫的推荐。

在实际挖掘的过程中,需要挖掘出的频繁项集满足一定的支持度。

支持度即为 A 和 B 项集在总体数据中出现的次数,设置支持度是为了过滤不值得注意的模式。假设电商场景中A 和 B 只共同购买过1次,那么在大量的电商数据中,这样的项集不值得引起注意。

下图中{A,B}的支持度为1,{A,C}的支持度为2.

订单 ID

item

1

{A , B , C}

2

{A, C}

3

{A}

Apriori

Apriori为传统的频繁项集挖掘算法,从单项集开始,经过不断迭代和剪枝,得到所有频繁项集。

对于上表中的例子,Apriori 先遍历所有数据统计所有的单项集得到每个项集的支持度{A}:3,{B}:1,{C}:2。

然后进行剪枝,减去低于支持度阈值的项集,假设支持度阈值 sup_thred = 2

那么{B}项集被减去。

通过剩余子集构造包含两个元素的项集得到{A,C}。然后遍历所有数据得到其支持度为2。

依次类推。剪枝使用的原理是频繁项集的支持度一定小于等于其子集的支持度,即sup({A}) >= sup({A,B}),所以子集无法满足阈值,那本身必定无法满足阈值。每轮通过剪纸后的子集构造新的项集。下图为 Apriori 剪枝,图中从 {a,b}项集支持度低于阈值,意味着其产生的项集的支持度都将低于阈值,可以在早期通过剪纸去掉。

java 频繁项集 频繁项集的例子_数据挖掘

值得注意的是:Apriori算法中,每轮迭代需要遍历一次全体数据,一旦待挖掘的频繁项集较长,且数据量较大则效率通常低下。FPGrowth 通过构造 FP 树解决了这个问题,在 FPGrowth 算法中,仅需要在构造 FP 树过程中遍历两次全局数据即可。

文章结构

下文将 FPGrowth 算法分为 FP 树搭建频繁项集挖掘两个部分。

(二)FP 树搭建

论文中算法步骤:

java 频繁项集 频繁项集的例子_逆序_02

算法步骤:

step1:

遍历数据,统计所有单项集的支持度,去掉低于支持度阈值的单项集,并将剩余的项按照支持度逆序排列。

java 频繁项集 频繁项集的例子_逆序_03

对于图中数据,统计结果为

{f}:4,{c}:4,{a}:3,{b}:3,{m}:3,{p}:3,{d}:1,{g}:1,{i}:1,{l}:2,{o}:2,{h}:1,{j}:1,{k}:1,{s}:1,{e}:1,{n}:1

由于支持度sup_thred 为2,剔除小于等于2的项集{d}:1,{g}:1,{i}:1,{l}:2,{o}:2,{h}:1,{j}:1,{k}:1,{s}:1,{e}:1,{n}:1。

剩余项集按逆序排列为 fcabmp。

step2:

先构造空的 FP 树T 以及表格 Header。表格 Header 保存了所有频繁项,可以通过链表访问到树 T 中同名的项,Header 表中按照项的降序排列。

然后遍历数据,对每条记录,将 step1中剔除的项剔除后,按照 step1给出的逆序顺序排列当前项集剩余的项。如上图中第二条记录 去除不频繁项{l,o}后逆序排列结果为 {f,c,a,b,m}。

java 频繁项集 频繁项集的例子_数据挖掘_04

对于每条记录,执行 insert([p|P],T)算法,我们将排序结果定义为(p|P),其中 p 代表的是排序结果头部项,P 则代表剩余项。对于排序{f,c,a,b,m},p为{f},而 P 为{c,a,m,p}。算法如下

 

insert([p|P],T)算法:

======================================

 if(T 的子节点中存在与 p 名称相同的节点){

         设置节点 N 为该节点,并使得 N 节点 count++;}

else{

          创建新的节点N保存数据 p;

          设置 N节点count=1;

          访问表格 Header,找到指向 p 的链表尾部,将当前节点 N 插入该链表尾部;}

if(P 非空){

调用 insert(P,N);}

======================================

insert([p|P],T)算法为递归算法,最终构造出完整的 FP 树,通过上图中的例子可自行熟练算法过程。

算法中的细节:

1.对项集进行排序再执行插入:如此才能共用前缀节点。

2.使用降序排列的原因:减少根节点的子节点数目。

 

(三)频繁项集挖掘

论文中算法步骤:

java 频繁项集 频繁项集的例子_逆序_05

算法步骤:

先解释算法中涉及到的构建 CPB 和构建条件 FP 树这两个操作。

构建 CPB:

对于 item i 来说,在FP 树中所有包含该元素的前缀路径即为此item 的 CPB。

举例来说,对于图中的 m ,其 CPB 即为包含 m 的两条前缀路径:fca 和 fcab。

java 频繁项集 频繁项集的例子_数据挖掘_04

由于构建 CPB 的过程是按照 header 表自底向上构建,所以元素 m 的后缀已经在之前的过程中考虑到了。构建 CPB 只考虑前缀路径。

构建条件 FP 树:

对于元素 m 来说,得到 CPB:{fca:2}和{fcab:1}之后,可以根据CPB 构建条件 FP树,即在包含元素 m 的前缀路径上构建新的 FP树,此即为条件 FP 树。

java 频繁项集 频繁项集的例子_数据挖掘_07

算法流程:

自底向上遍历 header 表,对于 header 表中的每个元素构建CPB 和条件 FP 树,然后将此条件 FP 树 T作为输入,执行 produce_FPGrowth(T,null)。

produce_FPGrowth(Tree T,pattern_base S):

======================================

if (T 包含一个单一的前缀路径){

    (1)设置 P 为该单一前缀路径, Q 则为分叉之后的树,其中分叉节点替换成 null。分叉节点包含在 P 中。(根节点不为空也视为含有单一前缀路径)

      举例:对于上图 m 的条件 FP 树来说, 将 P设置为{fca},Q 设置为由null 替代{a:3}后的树,包含{m:2},{b:1},{m:1}。

    (2)对于 P 中所有的元素,生成所有可能的组合。对于每个组合,其支持度为组合中支持度最小的节点。对于支持度大于阈值的组合,合并进入待输出的pattern集合中。

      举例:对于上图 m 的条件 FP 树,P为{fca},可以得到{f}:4,{c}:3,{a}:3,{fc}:3,{fa}:3,{ac}:3这六个在包含 m 的条件下的模式,将他们与 {m}组合得到六个频繁模式。

} else{

      设置 Q 为树T。

}

for (树 Q中的每个元素ai){

     (1)构造pattern belta,包含当前元素 {ai} 和条件{S},其支持度为元素 ai 的支持度。

       解释:在条件 FP树中,节点 ai中的数字就代表了{ai, 条件集合S}的支持度。 

      (2)使用belta构建新的条件 FP 树,

        if(新产生的条件 FP 树T2非空){

                   对于新创建的条件 FP 树,调用produce_FPGrowth(T2,belta)

         }

        举例:对于 m 的条件 FP 树,它的分叉树中包含

}

返回(1)Q 以及递归产生的频繁项集(2)P产生的频繁项集(3)Q 和 P 笛卡尔积组合产生的频繁项集,支持度为Q的支持度。

======================================

 

结论:

FPGrowth 的优点在于不需要多次遍历全局数据,而是通过两轮的遍历得到全局数据的FP 树后,在此基础上进行各种访问操作。

但对于数据量较小的场景来说,构建和处理 FP 树复杂度过高,并不适用。