`

FP-Tree算法的实现

 
阅读更多

在关联规则挖掘领域最经典的算法法是Apriori,其致命的缺点是需要多次扫描事务数据库。于是人们提出了各种裁剪(prune)数据集的方法以减少I/O开支,韩嘉炜老师的FP-Tree算法就是其中非常高效的一种。

支持度和置信度

严格地说Apriori和FP-Tree都是寻找频繁项集的算法,频繁项集就是所谓的“支持度”比较高的项集,下面解释一下支持度和置信度的概念。

设事务数据库为:

复制代码
A  E  F  G

A  F  G

A  B  E  F  G

E  F  G
复制代码

则{A,F,G}的支持度数为3,支持度为3/4。

{F,G}的支持度数为4,支持度为4/4。

{A}的支持度数为3,支持度为3/4。

{F,G}=>{A}的置信度为:{A,F,G}的支持度数 除以{F,G}的支持度数,即3/4

{A}=>{F,G}的置信度为:{A,F,G}的支持度数 除以{A}的支持度数,即3/3

强关联规则挖掘是在满足一定支持度的情况下寻找置信度达到阈值的所有模式。

FP-Tree算法

我们举个例子来详细讲解FP-Tree算法的完整实现。

事务数据库如下,一行表示一条购物记录:

复制代码
牛奶,鸡蛋,面包,薯片

鸡蛋,爆米花,薯片,啤酒

鸡蛋,面包,薯片

牛奶,鸡蛋,面包,爆米花,薯片,啤酒

牛奶,面包,啤酒

鸡蛋,面包,啤酒

牛奶,面包,薯片

牛奶,鸡蛋,面包,黄油,薯片

牛奶,鸡蛋,黄油,薯片
复制代码

我们的目的是要找出哪些商品总是相伴出现的,比如人们买薯片的时候通常也会买鸡蛋,则[薯片,鸡蛋]就是一条频繁模式(frequent pattern)。

FP-Tree算法第一步:扫描事务数据库,每项商品按频数递减排序,并删除频数小于最小支持度MinSup的商品。(第一次扫描数据库)

薯片:7鸡蛋:7面包:7牛奶:6啤酒:4 (这里我们令MinSup=3)

以上结果就是频繁1项集,记为F1。

第二步:对于每一条购买记录,按照F1中的顺序重新排序。(第二次也是最后一次扫描数据库)

复制代码
薯片,鸡蛋,面包,牛奶

薯片,鸡蛋,啤酒

薯片,鸡蛋,面包

薯片,鸡蛋,面包,牛奶,啤酒

面包,牛奶,啤酒

鸡蛋,面包,啤酒

薯片,面包,牛奶

薯片,鸡蛋,面包,牛奶

薯片,鸡蛋,牛奶
复制代码

第三步:把第二步得到的各条记录插入到FP-Tree中。刚开始时后缀模式为空。

插入每一条(薯片,鸡蛋,面包,牛奶)之后

插入第二条记录(薯片,鸡蛋,啤酒)

插入第三条记录(面包,牛奶,啤酒)

估计你也知道怎么插了,最终生成的FP-Tree是:

上图中左边的那一叫做表头项,树中相同名称的节点要链接起来,链表的第一个元素就是表头项里的元素。

如果FP-Tree为空(只含一个虚的root节点),则FP-Growth函数返回。

此时输出表头项的每一项+postModel,支持度为表头项中对应项的计数。

第四步:从FP-Tree中找出频繁项。

遍历表头项中的每一项(我们拿“牛奶:6”为例),对于各项都执行以下(1)到(5)的操作:

(1)从FP-Tree中找到所有的“牛奶”节点,向上遍历它的祖先节点,得到4条路径:

复制代码
薯片:7,鸡蛋:6,牛奶:1

薯片:7,鸡蛋:6,面包:4,牛奶:3

薯片:7,面包:1,牛奶:1

面包:1,牛奶:1
复制代码

对于每一条路径上的节点,其count都设置为牛奶的count

复制代码
薯片:1,鸡蛋:1,牛奶:1

薯片:3,鸡蛋:3,面包:3,牛奶:3

薯片:1,面包:1,牛奶:1

面包:1,牛奶:1
复制代码

因为每一项末尾都是牛奶,可以把牛奶去掉,得到条件模式基(Conditional Pattern Base,CPB),此时的后缀模式是:(牛奶)。

复制代码
薯片:1,鸡蛋:1

薯片:3,鸡蛋:3,面包:3

薯片:1,面包:1

面包:1
复制代码

(2)我们把上面的结果当作原始的事务数据库,返回到第3步,递归迭代运行。

没讲清楚,你可以参考这篇博客,直接看核心代码吧:

复制代码
public void FPGrowth(List<List<String>> transRecords,
        List<String> postPattern,Context context) throws IOException, InterruptedException {
    // 构建项头表,同时也是频繁1项集
    ArrayList<TreeNode> HeaderTable = buildHeaderTable(transRecords);
    // 构建FP-Tree
    TreeNode treeRoot = buildFPTree(transRecords, HeaderTable);
    // 如果FP-Tree为空则返回
    if (treeRoot.getChildren()==null || treeRoot.getChildren().size() == 0)
        return;
    //输出项头表的每一项+postPattern
    if(postPattern!=null){
        for (TreeNode header : HeaderTable) {
            String outStr=header.getName();
            int count=header.getCount();
            for (String ele : postPattern)
                outStr+="\t" + ele;
            context.write(new IntWritable(count), new Text(outStr));
        }
    }
    // 找到项头表的每一项的条件模式基,进入递归迭代
    for (TreeNode header : HeaderTable) {
        // 后缀模式增加一项
        List<String> newPostPattern = new LinkedList<String>();
        newPostPattern.add(header.getName());
        if (postPattern != null)
            newPostPattern.addAll(postPattern);
        // 寻找header的条件模式基CPB,放入newTransRecords中
        List<List<String>> newTransRecords = new LinkedList<List<String>>();
        TreeNode backnode = header.getNextHomonym();
        while (backnode != null) {
            int counter = backnode.getCount();
            List<String> prenodes = new ArrayList<String>();
            TreeNode parent = backnode;
            // 遍历backnode的祖先节点,放到prenodes中
            while ((parent = parent.getParent()).getName() != null) {
                prenodes.add(parent.getName());
            }
            while (counter-- > 0) {
                newTransRecords.add(prenodes);
            }
            backnode = backnode.getNextHomonym();
        }
        // 递归迭代
        FPGrowth(newTransRecords, newPostPattern,context);
    }
}
复制代码

对于FP-Tree已经是单枝的情况,就没有必要再递归调用FPGrowth了,直接输出整条路径上所有节点的各种组合+postModel就可了。例如当FP-Tree为:

我们直接输出:

3  A+postModel

3  B+postModel

3  A+B+postModel

就可以了。

如何按照上面代码里的做法,是先输出:

3  A+postModel

3  B+postModel

然后把B插入到postModel的头部,重新建立一个FP-Tree,这时Tree中只含A,于是输出

3  A+(B+postModel)

两种方法结果是一样的,但毕竟重新建立FP-Tree计算量大些。

Java实现

FP树节点定义

挖掘频繁模式

输入文件

复制代码
牛奶,鸡蛋,面包,薯片
鸡蛋,爆米花,薯片,啤酒
鸡蛋,面包,薯片
牛奶,鸡蛋,面包,爆米花,薯片,啤酒
牛奶,面包,啤酒
鸡蛋,面包,啤酒
牛奶,面包,薯片
牛奶,鸡蛋,面包,黄油,薯片
牛奶,鸡蛋,黄油,薯片
复制代码

输出

复制代码
6    薯片    鸡蛋
5    薯片    面包
5    鸡蛋    面包
4    薯片    鸡蛋    面包
5    薯片    牛奶
5    面包    牛奶
4    鸡蛋    牛奶
4    薯片    面包    牛奶
4    薯片    鸡蛋    牛奶
3    面包    鸡蛋    牛奶
3    薯片    面包    鸡蛋    牛奶
3    鸡蛋    啤酒
3    面包    啤酒
复制代码

用Hadoop来实现

在上面的代码我们把整个事务数据库放在一个List<List<String>>里面传给FPGrowth,在实际中这是不可取的,因为内存不可能容下整个事务数据库,我们可能需要从关系关系数据库中一条一条地读入来建立FP-Tree。但无论如何FP-Tree是肯定需要放在内存中的,但内存如果容不下怎么办?另外FPGrowth仍然是非常耗时的,你想提高速度怎么办?解决办法:分而治之,并行计算。

我们把原始事务数据库分成N部分,在N个节点上并行地进行FPGrowth挖掘,最后把关联规则汇总到一起就可以了。关键问题是怎么“划分”才会不遗露任何一条关联规则呢?参见这篇博客。这里为了达到并行计算的目的,采用了一种“冗余”的划分方法,即各部分的并集大于原来的集合。这种方法最终求出来的关联规则也是有冗余的,比如在节点1上得到一条规则(6:啤酒,尿布),在节点2上得到一条规则(3:尿布,啤酒),显然节点2上的这条规则是冗余的,需要采用后续步骤把冗余的规则去掉。

代码:

Record.java

DC_FPTree.java

结束语

在实践中,关联规则挖掘可能并不像人们期望的那么有用。一方面是因为支持度置信度框架会产生过多的规则,并不是每一个规则都是有用的。另一方面大部分的关联规则并不像“啤酒与尿布”这种经典故事这么普遍。关联规则分析是需要技巧的,有时需要用更严格的统计学知识来控制规则的增殖。 

原文来自:博客园(华夏35度)http://www.cnblogs.com/zhangchaoyang作者:Orisun
分享到:
评论

相关推荐

    FP-TREE算法 C++实现

    这个C++实现版本提供了FP-TREE算法的完整功能,包括构建FP树、查找频繁项集以及进行关联规则挖掘。 在数据挖掘中,频繁项集是指在数据集中出现次数超过预设阈值的项集合。FP-TREE算法通过构建一棵特殊的树结构来...

    数据挖掘Apriori和FP-tree算法的实现

    在数据挖掘中,关联规则学习是探索数据中项集之间有趣关系的重要方法,而Apriori和FP-growth(FP-tree)是两种经典的关联规则挖掘算法。 **Apriori算法** 是最早被提出的关联规则挖掘算法之一,由Rakesh Agrawal和...

    fp-tree算法程序

    下面将详细解释FP-Tree算法的核心原理及其在C#中的实现。 **FP-Tree算法概述:** FP-Tree算法由Han等人于2000年提出,主要用于关联规则学习。它的主要目的是通过构建一棵特殊的树结构(FP-Tree)来降低内存消耗和...

    python实现FP-TREE挖掘算法

    Python作为一种强大的编程语言,因其易读性与丰富的库支持,成为了实现FP-TREE算法的理想选择。在这个项目中,我们使用Python 3.2版本来实现这一算法,并且能可视化每一步的FP树,帮助用户更好地理解和分析过程。 ...

    FP-Tree.zip_ fp tree_fp tree_fp-growth_fp-growth算法的源代码_fp-tree

    FP-Tree算法的核心思想是通过构建一棵倒置的树形结构来存储交易数据中的频繁项,以此来减少内存占用和计算复杂性。以下是FP-Tree的基本步骤: 1. **预处理**:首先,对输入数据进行一次遍历,找出所有出现过的项,...

    FP-Tree算法介绍文档

    ### FP-Tree算法详解 #### 一、引言 FP-Tree(Frequent Pattern Tree)是一种高效的挖掘频繁项集的数据结构。与传统的Apriori算法相比,它通过减少数据库扫描次数以及候选集的数量来提高效率。本文档旨在为初学者...

    数据挖掘\fp-tree算法

    fp-tree算法的核心在于高效地处理频繁项集(frequent itemsets),即在数据集中出现次数超过预设阈值的项目组合。该算法主要分为两个阶段:构建fp树和挖掘关联规则。 1. 构建fp树: - 预处理阶段:首先,对数据集...

    详解python实现FP-TREE进行关联规则挖掘

    本文将深入探讨如何使用Python来实现FP-TREE算法,并通过Python3.2进行演示。为了可视化FP树的生成过程,需要安装PIL库来处理图像。 首先,我们需要理解关联规则挖掘的基本概念。关联规则通常由两部分组成:前提...

    C语言实现的FP-growth算法

    在压缩包"FP_growth"中,可能包含了实现这些功能的源代码文件,例如`fp_tree.c`用于构建FP树,`fp_growth.c`实现了算法的核心部分,`util.c`可能包含了辅助函数,如数据读取和内存管理等。通过阅读和理解这些代码,...

    fp.rar_FP-Growth算法_fp_fp tree_fp-growth_fp-tree

    这个算法由Han, Pei和Jianmin Wang于2000年提出,它通过构建一种特殊的树结构——频繁模式树(FP-Tree)来减少计算量,从而提高效率。 FP-Growth的基本步骤可以分为三个主要部分: 1. **预处理**:首先,对数据集...

    FP-Tree GDI

    以下是对FP-Tree算法的详细解释: 1. **构建过程**:首先,对原始数据集进行预处理,计算每个项的频率。然后,按照项的降序排列,将交易数据插入FP-Tree。每次插入时,会在树中创建一个新的节点,该节点包含当前项...

    fpgrowth_source.rar_FP-Growth算法_fp-growth_fp-tree_fpgrowth_sourc

    这个算法的主要优点是高效性和内存效率,它通过构建FP-Tree(频繁模式树)来减少对原始数据的扫描次数,从而大大降低了计算复杂度。 FP-Growth算法的核心步骤如下: 1. **数据预处理**:首先,对交易数据集进行一...

    FP-Growth算法python实现(完整代码)

    包含两个文件,一个是刚构造好FP-tree的代码,另一个是FP-Growth算法python实现的完全代码。更多的介绍请见博客:http://blog.csdn.net/bone_ace/article/details/46746727

    fp-tree.rar_fp_fp-tree_tree_频繁项集

    3. **可扩展性**:fp-tree算法可以与其他挖掘任务结合,如关联规则学习,通过在fp-tree基础上寻找强规则。 **fp-tree的局限性与改进**: 尽管fp-tree算法在很多情况下表现优秀,但也有其局限性。例如,对于项集支持...

    FP-TREE.rar_FP-tree java_tree_频繁项集

    FP-TREE.rar 是一个压缩包,其中包含了使用Java语言实现FP-Tree算法的代码,用于挖掘数据中的频繁项集。FP-Tree(Frequent Pattern Tree)是一种数据挖掘中用于高效发现频繁项集的结构,特别是在大规模交易数据集中...

    fp-tree java源码

    ### FP-Tree算法详解及其Java源码实现 在数据挖掘领域,尤其是关联规则挖掘中,寻找频繁模式是一项核心任务。传统的Apriori算法虽然有效,但因需多次扫描数据库而存在效率瓶颈。针对这一问题,韩嘉炜教授提出的FP-...

    fp-tree详细介绍

    - **效率**:FP-Tree算法减少了数据库扫描次数,降低了内存需求,因为存储的是压缩的树结构,而非原始交易数据。 - **适应性**:FP-Tree可以与其他数据挖掘任务结合,如关联规则学习,通过发现频繁项集来生成规则。 ...

    FP_Tree.rar_ FP_tree _fp-tree_fp_tree_fp算法_tree

    FP-Tree算法是一种在数据挖掘领域中用于频繁模式发现的高效方法,特别是在处理大规模交易数据库时。这个压缩包“FP_Tree.rar”包含了FP-Tree算法的源代码,可以帮助我们理解和应用这一技术。以下是对FP-Tree算法的...

    C++版的fp-growth算法

    **C++实现的FP-Growth算法** FP-Growth算法是一种高效的数据挖掘技术,主要用于关联规则学习,即在大规模数据集中发现频繁项集。这个算法在处理大数据量时表现出了较高的性能,因为它避免了构建和搜索全频繁项集的...

Global site tag (gtag.js) - Google Analytics