关联分析:FP-Growth算法

转载自  关联分析:FP-Growth算法

关联分析又称关联挖掘,就是在交易数据、关系数据或其他信息载体中,查找存在于项目集合或对象集合之间的频繁模式、关联、相关性或因果结构。关联分析的一个典型例子是购物篮分析。通过发现顾客放入购物篮中不同商品之间的联系,分析顾客的购买习惯。比如,67%的顾客在购买尿布的同时也会购买啤酒。通过了解哪些商品频繁地被顾客同时购买,可以帮助零售商制定营销策略。关联分析也可以应用于其他领域,如生物信息学、医疗诊断、网页挖掘和科学数据分析等。

 

1. 问题定义

图1 购物篮数据的二元表示

  图1表示顾客的购物篮数据,其中每一行是每位顾客的购物记录,对应一个事务,而每一列对应一个项。令I={i1, i2, ... , id}是购物篮数据中所有项的集合,而T={t1, t2, ... , tN}是所有事务的集合。每个事务ti包含的项集都是I的子集。在关联分析中,包含0个或多个项的集合被称为项集(itemset)。所谓的关联规则是指形如X→Y的表达式,其中X和Y是不相交的项集。在关联分析中,有两个重要的概念——支持度(support)和置信度(confidence)。支持度确定规则可以用于给定数据集的频繁程度,而置信度确定Y在包含X的事务中出现的频繁程度。支持度(s)和置信度(c)这两种度量的形式定义如下:

公式1

  其中,N是事务的总数。关联规则的支持度很低,说明该规则只是偶然出现,没有多大意义。另一方面,置信度可以度量通过关联规则进行推理的可靠性。因此,大多数关联分析算法采用的策略是:

(1)频繁项集产生:其目标是发现满足最小支持度阈值的所有项集,这些项集称作频繁项集。

(2)规则的产生:其目标是从上一步发现的频繁项集中提取所有高置信度的规则,这些规则称作强规则。

 

2. 构建FP-tree

  FP-growth算法通过构建FP-tree来压缩事务数据库中的信息,从而更加有效地产生频繁项集。FP-tree其实是一棵前缀树,按支持度降序排列,支持度越高的频繁项离根节点越近,从而使得更多的频繁项可以共享前缀。

图2 事务型数据库

  图2表示用于购物篮分析的事务型数据库。其中,a,b,...,p分别表示客户购买的物品。首先,对该事务型数据库进行一次扫描,计算每一行记录中各种物品的支持度,然后按照支持度降序排列,仅保留频繁项集,剔除那些低于支持度阈值的项,这里支持度阈值取3,从而得到<(f:4),(c:4),(a:3),(b:3),(m:3,(p:3)>(由于支持度计算公式中的N是不变的,所以仅需要比较公式中的分子)。图2中的第3列展示了排序后的结果。

  FP-tree的根节点为null,不表示任何项。接下来,对事务型数据库进行第二次扫描,从而开始构建FP-tree:

  第一条记录<f,c,a,m,p>对应于FP-tree中的第一条分支<(f:1),(c:1),(a:1),(m:1),(p:1)>:

图3 第一条记录

  由于第二条记录<f,c,a,b,m>与第一条记录有相同的前缀<f,c,a>,因此<f,c,a>的支持度分别加一,同时在(a:2)节点下添加节点(b:1),(m:1)。所以,FP-tree中的第二条分支是<(f:2),(c:2),(a:2),(h:1),(m:1)>:

图4 第二条记录

  第三条记录<f,b>与前两条记录相比,只有一个共同前缀<f>,因此,只需要在(f:3)下添加节点<b:1>:

图5 第三条记录

  第四条记录<c,b,p>与之前所有记录都没有共同前缀,因此在根节点下添加节点(c:1),(b:1),(p:1):

图6 第四条记录

  类似地,将第五条记录<f,c,a,m,p>作为FP-tree的一个分支,更新相关节点的支持度:

图7 第五条记录

   为了便于对整棵树进行遍历,建立一张项的头表(an item header table)。这张表的第一列是按照降序排列的频繁项。第二列是指向该频繁项在FP-tree中节点位置的指针。FP-tree中每一个节点还有一个指针,用于指向相同名称的节点:

图8 FP-tree

  综上,FP-tree的节点可以定义为:

1
2
3
4
5
6
7
8
9
10
11
class TreeNode {
private:
    String name; // 节点名称
    int count; // 支持度计数
    TreeNode *parent; // 父节点
    Vector<TreeNode *> children; // 子节点
    TreeNode *nextHomonym; // 指向同名节点
     
    ...
}

 

3. 从FP-tree中挖掘频繁模式(Frequent Patterns)

  我们从头表的底部开始挖掘FP-tree中的频繁模式。在FP-tree中以p结尾的节点链共有两条,分别是<(f:4),(c:3),(a:3),(m:2),(p:2)>和<(c:1),(b:1),(p:1)>。其中,第一条节点链表表示客户购买的物品清单<f,c,a,m,p>在数据库中共出现了两次。需要注意到是,尽管<f,c,a>在第一条节点链中出现了3次,单个物品<f>出现了4次,但是它们与p一起出现只有2次,所以在条件FP-tree中将<(f:4),(c:3),(a:3),(m:2),(p:2)>记为<(f:2),(c:2),(a:2),(m:2),(p:2)>。同理,第二条节点链表示客户购买的物品清单<c,b,p>在数据库中只出现了一次。我们将p的前缀节点链<(f:2),(c:2),(a:2),(m:2)>和<(c:1),(b:1)>称为p的条件模式基(conditional pattern base)。我们将p的条件模式基作为新的事务数据库,每一行存储p的一个前缀节点链,根据第二节中构建FP-tree的过程,计算每一行记录中各种物品的支持度,然后按照支持度降序排列,仅保留频繁项集,剔除那些低于支持度阈值的项,建立一棵新的FP-tree,这棵树被称之为p的条件FP-tree:

图9 p的条件FP-tree

  从图9可以看到p的条件FP-tree中满足支持度阈值的只剩下一个节点(c:3),所以以p结尾的频繁项集有(p:3),(cp:3)。由于c的条件模式基为空,所以不需要构建c的条件FP-tree。

  在FP-tree中以m结尾的节点链共有两条,分别是<(f:4),(c:3),(a:3),(m:2)>和<(f:4),(c:3),(a:3),(b:1),(m:1)>。所以m的条件模式基是<(f:2),(c:2),(a:2)>和<(f:1),(c:1),(a:1),(b:1)>。我们将m的条件模式基作为新的事务数据库,每一行存储m的一个前缀节点链,计算每一行记录中各种物品的支持度,然后按照支持度降序排列,仅保留频繁项集,剔除那些低于支持度阈值的项,建立m的条件FP-tree:

图10 m的条件FP-tree

  与p不同,m的条件FP-tree中有3个节点,所以需要多次递归地挖掘频繁项集mine(<(f:3),(c:3),(a:3)|(m:3)>)。按照<(a:3),(c:3),(f:3)>的顺序递归调用mine(<(f:3),(c:3)|a,m>),mine(<(f:3)|c,m>),mine(null|f,m)。由于(m:3)满足支持度阈值要求,所以以m结尾的频繁项集有{(m:3)}。

图11 节点(a,m)的条件FP-tree

  从图11可以看出,节点(a,m)的条件FP-tree有2个节点,需要进一步递归调用mine(<(f:3)|c,a,m>)和mine(<null|f,a,m>)。进一步递归mine(<(f:3)|c,a,m>)生成mine(<null|f,c,a,m>)。因此,以(a,m)结尾的频繁项集有{(am:3),(fam:3),(cam:3),(fcam:3)}。

  

图 12 节点(c,m)的条件FP-tree

  从图12可以看出,节点(c,m)的条件FP-tree只有1个节点,所以只需要递归调用mine(<null|f,c,m>)。因此,以(c,m)结尾的频繁项集有{(cm:3),(fcm:3)}。同理,以(f,m)结尾的频繁项集有{(fm:3)}。

  在FP-tree中以b结尾的节点链共有三条,分别是<(f:4),(c:3),(a:3),(b:1)>,<(f:4),(b:1)>和<(c:1),(b:1)>。由于节点b的条件模式基<(f:1),(c:1),(a:1)>,<(f:1)>和<(c:1)>都不满足支持度阈值,所以不需要再递归。因此,以b结尾的频繁项集只有(b:3)。

  同理可得,以a结尾的频繁项集{(fa:3),(ca:3),(fca:3),(a:3)},以c结尾的频繁项集{(fc:3),(c:4)},以f结尾的频繁项集{(f:4)}。

 

4. 算法实现

声明FP-tree节点:

class TreeNode
{//Constructors-Destructors
public:TreeNode();TreeNode(string);~TreeNode();//Member variables
private:string nodeName;int supportCount;TreeNode *parentNode;vector<TreeNode *> childNodeList;TreeNode *nextHomonymNode;//Member functions
public:string getName();void setName(string);int getSupportCount() const;void setSupportCount(int);TreeNode* getParentNode() const;void setParentNode(TreeNode*);vector<TreeNode*> getChildNodeList() const;void addChild(TreeNode*);TreeNode* findChildNode(string) const;void setChildren(vector<TreeNode*>);void printChildrenNames() const;TreeNode* getNextHomonym() const;void setNextHomonym(TreeNode *nextHomonym);void countIncrement(int);
};

构建HeaderTable:

//HeaderTable存储事务数据库的数据
vector<TreeNode*> FPTree::buildHeaderTable(vector<vector<string>> transRecords)
{vector<TreeNode*> F1; //存储满足支持度阈值的节点,并按照支持度降序排列,支持度相等的情况下按照字母顺序排序,所以构建的FP-tree与论文有所不同,但是最终生成的频繁项集是一样的if (transRecords.size() > 0){map<string, TreeNode*> mp;//calculate supportCount of every transRecordsfor (vector<string> record : transRecords){for (string item : record){//if item not in map, put item into map and set supportCount oneif (mp.find(item) == mp.end()){TreeNode *node = new TreeNode(item);node->setSupportCount(1);mp.insert(map<string, TreeNode*>::value_type(item, node));}//if item in map, supportCount plus one else{mp.find(item)->second->countIncrement(1);}}}//put TreeNodes whose supportCount greater than minSupportThreshold into vector F1for (auto iterator = mp.begin(); iterator != mp.end(); iterator++){if (iterator->second->getSupportCount() >= minSupportThreshold){//cout << "iterator->second = " << iterator->second->getSupportCount() << endl;F1.push_back(iterator->second);}}//sort vector F1 by supportCount
        sort(F1.begin(), F1.end(), sortBySupportCount);}return F1;
}

构建FP-tree:

TreeNode* FPTree::buildTree(vector<vector<string>> transRecords, vector<TreeNode*> F1)
{TreeNode *root = new TreeNode(); //根节点rootfor (vector<string> transRecord : transRecords){//拷贝transRecord到record    vector<string> record;for (auto iter = transRecord.begin(); iter != transRecord.end(); iter++){record.push_back(*iter);}record = sortedByF1(record, F1); //根据F1中存储的频繁项集,将record按照支持度降序排列,并且仅保留频繁项集,剔除那些低于支持度阈值的项

//顺序比较record中的节点和FP-tree中的节点,如果record中的节点已经存在于FP-tree中,将该节点的支持度加一,继续比较下一个节点,否则调用addNodes来添加剩余节点到FP-tree中TreeNode *subTreeRoot = root;TreeNode *tmpRoot = nullptr;if (!root->getChildNodeList().empty()){while (!record.empty() && (tmpRoot = subTreeRoot->findChildNode(*(record.begin()))) != nullptr){tmpRoot->countIncrement(1);subTreeRoot = tmpRoot;record.erase(record.begin());}}addNodes(subTreeRoot, &record, F1);}return root; }

添加节点:

void FPTree::addNodes(TreeNode *ancestor, vector<string> *record, vector<TreeNode*> F1) 
{if (!record->empty()){while (!record->empty()){string item = *(record->begin());record->erase(record->begin());TreeNode *leafNode = new TreeNode(item);leafNode->setSupportCount(1);leafNode->setParentNode(ancestor);ancestor->addChild(leafNode);for (TreeNode *f1 : F1){if (f1->getName() == item){ while (f1->getNextHomonym() != NULL){f1 = f1->getNextHomonym();}f1->setNextHomonym(leafNode);break;}}addNodes(leafNode, record, F1);}}
}

sortedByF1:

vector<string> FPTree::sortedByF1(vector<string> transRecord, vector<TreeNode*> F1)
{//如果item是频繁项,则一定对应于F1中的序号,按照该序号对item进行排序,存储到rest中map<string, int> mp;for (string item : transRecord){for (int i = 0; i < F1.size(); i++){TreeNode *tNode = F1[i];if (tNode->getName() == item){mp.insert(map<string, int>::value_type(item, i));}}}vector<pair<string, int>> vec;for (auto iterator = mp.begin(); iterator != mp.end(); iterator++){vec.push_back(make_pair(iterator->first, iterator->second));}sort(vec.begin(), vec.end(), sortByF1);vector<string> rest;for (auto iterator = vec.begin(); iterator != vec.end(); iterator++){rest.push_back((*iterator).first);}return rest;
}

递归调用FP-Growth挖掘频繁项:

//postPattern存储后缀,比如从HeaderTable中的p节点开始挖掘频繁项时,postPattern为p
void FPTree::FPGrowth(vector<vector<string>> transRecords, vector<string> postPattern)
{vector<TreeNode*> headerTable = buildHeaderTable(transRecords); //构建headerTableTreeNode *treeRoot = buildTree(transRecords, headerTable); //构建FP-tree//递归退出条件:根节点没有孩子节点if (treeRoot->getChildNodeList().size() == 0) {return;}
//输出频繁项集if (!postPattern.empty()){for (TreeNode *header : headerTable){cout << header->getSupportCount() << ends << header->getName() << ends;for (string str : postPattern){cout << str << ends;}cout << endl;}}//遍历headerTable
    for (TreeNode *header : headerTable){vector<string> newPostPattern;newPostPattern.push_back(header->getName());//存储原先的后缀
        if (!postPattern.empty()) {for (string str : postPattern){newPostPattern.push_back(str);}}
//newTransRecords存储前缀节点链
        vector<vector<string>> newTransRecords;TreeNode *backNode = header->getNextHomonym();//通过getNextHomonym遍历同名节点,通过getParentNode获取前缀节点链
        while (backNode != nullptr){int supportCount = backNode->getSupportCount();vector<string> preNodes;TreeNode *parent = backNode; 
while ((parent = parent->getParentNode())->getName().length() != 0){preNodes.push_back(parent->getName());} while (supportCount-- > 0){newTransRecords.push_back(preNodes);}backNode = backNode->getNextHomonym();
}FPGrowth(newTransRecords, newPostPattern); //递归构建条件FP-tree } }

 

5. 讨论

  在韩家炜教授提出FP-growth算法之前,关联分析普遍采用Apriori及其变形算法。但是,Apriori及其变形算法需要多次扫描数据库,并需要生成指数级的候选项集,性能并不理想。FP-growth算法提出利用了高效的数据结构FP-tree,不再需要多次扫描数据库,同时也不再需要生成大量的候选项。

  对于单路径的FP-tree其实不需要递归,通过排列组合可以直接生成。韩家炜教授在其论文中提到了针对单路径的优化算法。论文中也提到了面对大数据时,如何调整FP-growth算法使之适应数据量。

 

6. 参考资料

[1] Mining Frequent Patterns without Candidate Generation. Jiawei Han, Jian Pei, and Yiwen Yin. Data Mining and Knowledge Discovery. Volume 8 Issue 1. January 2004. [PDF]

[2] Frequent Pattern 挖掘之二(FP Growth算法). yfx416. Software Engineer in NRC. 2011. [Link]

[3] FP-Tree算法的实现. Orisun. 华夏35度. 2011. [Link]


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328830.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql server链接查询

一、连接结果集中有多个表的信息时用连接查询1.内连接:多个表根据公共列连接&#xff0c;符合条件的显示&#xff0c;不符合条件的不显示 2.外连接:多个表根据公共列连接&#xff0c;显示一个表中的所有信息&#xff0c;另个表中中符合条件的信息&#xff0c;不符合条件的用nul…

ASP.NET Core 中文文档 第三章 原理(1)应用程序启动

原文&#xff1a;Application Startup作者&#xff1a;Steve Smith翻译&#xff1a;刘怡(AlexLEWIS)校对&#xff1a;谢炀(kiler398)、许登洋(Seay) ASP.NET Core 为你的应用程序提供了处理每个请求的完整控制。Startup 类是应用程序的入口&#xff08;entry point&#xff09;…

基于内容的推荐(Content-based Recommendations)

转载自 基于内容的推荐&#xff08;Content-based Recommendations&#xff09;Collaborative Filtering Recommendations (协同过滤&#xff0c;简称CF) 是目前最流行的推荐方法&#xff0c;在研究界和工业界得到大量使用。但是&#xff0c;工业界真正使用的系统一般都不会只…

[置顶]动态网页开发基础【笔记】

一、C/S结构和B/S结构1.C/S&#xff08;Client/Server&#xff09;:客户端服务程序&#xff0c;控制台程序&#xff0c;window应用2.B/S(Browser/Server):浏览器服务程序[java:jsp应用&#xff1b;C#:asp.net],web应用程序区别&#xff1a;C/S:客户端和服务器端都需要开发&…

ASP.NET Core 中文文档 第三章 原理(3)静态文件处理

原文&#xff1a;Working with Static Files作者&#xff1a;Rick Anderson翻译&#xff1a;刘怡(AlexLEWIS)校对&#xff1a;谢炀(kiler398)、许登洋(Seay)、孟帅洋(书缘) 静态文件&#xff08;static files&#xff09;&#xff0c;诸如 HTML、CSS、图片和 JavaScript 之类的…

使用IDEA Maven搭建Mybatis环境

本文是使用IDEA Maven搭建Mybatis环境 涉及到的搭配环境 JDK1.8 Maven3.6.3 IDEA2019 MySQL数据库 一、Maven介绍 Maven 是一个项目管理工具&#xff0c;可以对 Java 项目进行构建、依赖管理&#xff0c;是一个自动化构建工具。 自动化构建工具&#xff1a;将原材料&…

常用推荐算法

转载自 常用推荐算法在推荐系统简介中&#xff0c;我们给出了推荐系统的一般框架。很明显&#xff0c;推荐方法是整个推荐系统中最核心、最关键的部分&#xff0c;很大程度上决定了推荐系统性能的优劣。目前&#xff0c;主要的推荐方法包括&#xff1a;基于内容推荐、协同过滤…

不该活着的SqlHelper和DBHelper

前言&#xff1a; 还记得刚学ADO.NET的情景么&#xff1f; 还记得当年是怎么从ADO.NET被忽悠到用SqlHelper的么&#xff1f; 话说从入门到走上工作岗位那些年&#xff0c;我们就一直被纯纯地教导或引导&#xff0c;ADO.NET太原始&#xff0c;得封装成SqlHelper或DBHelper......…

使用IDEA配置Mybatis-Plus框架

使用IDEA配置Mybatis-Plus框架 本文是以使用IDEA配置Mybatis-Plus框架作为简单的讲解。 所涉及到的应用&#xff1a; IDEA2019Mybatis-Plus框架Mysql数据库Maven3.6.3jdk1.8 一、什么是Mybatis-Plus框架&#xff1f; MyBatis-Plus(简称MP)是一个MyBatis的增强工具&#xff…

css实现一级下拉菜单

涉及到的图片请到这里下载 涉及到的图片请到这里下载 涉及到的图片请到这里下载 html代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"…

UserCF,基于用户的协同过滤算法

转载自 UserCF,基于用户的协同过滤算法UserCF&#xff1a;User Collaboration Filter&#xff0c;基于用户的协同过滤 算法核心思想&#xff1a;在一个在线推荐系统中&#xff0c;当用户A需要个性化推荐时&#xff0c;可以先找到和他有相似兴趣的其它用户&#xff0c;然后…

用BenchmarkDotNet给C#程序做性能测试

BenchmarkDotNet是一个用MIT协议开源的C#程序性能测试的一个库&#xff0c;非常简单易用。 用法 安装NuGet包&#xff0c;BenchmarkDotNet在需要做性能测试的方法前加上属性[Benchmark]。在Main函数调用性能测试var summary BenchmarkRunner.Run<Md5VsSha256>();。 工作…

ItemCF,基于物品的协同过滤算法

转载自 ItemCF,基于物品的协同过滤算法ItemCF&#xff1a;Item Collaboration Filter&#xff0c;基于物品的协同过滤 算法核心思想&#xff1a;给用户推荐那些和他们之前喜欢的物品相似的物品。 比如&#xff0c;用户A之前买过《数据挖掘导论》&#xff0c;该算法会根据此行…

用Swashbuckle给ASP.NET Core的项目自动生成Swagger的API帮助文档

Swagger是一个描述RESTful的Web API的规范和框架。如果使用ASP.NET的话&#xff0c;可以用Swashbuckle来自动生成Swagger,具体参考如何使 WebAPI 自动生成漂亮又实用在线API文档。下面详细的介绍一下如何给ASP.NET Core的项目自动生成Swagger的API帮助文档。 创建ASP.NET Core的…

涨知识 | 100个中国最难读的地名,第一个就折了

内容来源网络&#xff0c;侵删&#xff01; 1.浙江丽水的丽&#xff0c;不读l&#xff0c;读l。 2.浙江台州、天台的台&#xff0c;不读ti&#xff0c;读tāi。 3.浙江嵊州的嵊&#xff0c;读音为shng。 4.浙江鄞县的鄞&#xff0c;读音为yn&#xff0c;不读jn。 5.浙江乐清的乐…

JSP 教程

JSP 教程JSP 教程JSP 简介JSP 开发环境搭建Eclipse JSP/ServletJSP 结构JSP 生命周期JSP 语法JSP 指令JSP 动作元素JSP 隐式对象JSP 客户端请求JSP 服务器响应JSP HTTP 状态码JSP 表单处理JSP 过滤器JSP Cookie 处理JSP SessionJSP 文件上传JSP 日期处理JSP 页面重定向JSP 点击…

IronPython项目有了新负责人

运行IronPython项目的社区领导职责已经交给了Alex Earl和Benedikt Eggers。之前的负责人Jeff Hardy自微软在2010年不再积极参与以来一直负责项目的运行。 Jeff Hardy写道&#xff1a; 应该已经相当明显了&#xff0c;IronPython项目有点停滞不前&#xff0c;那主要是我的错&…

修改Tomcat编码方式的两种方法

转载自 修改Tomcat编码方式的两种方法方法一&#xff1a;推荐&#xff0c;不会影响到其它项目 见我的另一篇博客&#xff1a;http://www.cnblogs.com/x_wukong/p/3292664.html 修改方法&#xff1a; 修改tomcat下的conf/server.xml文件&#xff0c;找到Connector标签&#x…

Microsoft宣布.NET Core支持计划

微软宣布针对.NET Core、ASP.NET Core和Entity Framework Core的支持计划。据来自微软的Lee Coward介绍&#xff0c;公司计划使用一种双管齐下的方法来管理这些系统的发布。该计划回答了微软开源计划背后的其中一个主要问题——开发人员可以在多长时间内依赖微软向这些年轻的项…

《机器学习项目开发实战》送书活动结果公布

截止到8月8日24&#xff1a;00&#xff0c;本次送书活动 共收到70位同学参与回复&#xff0c;本次很多同学在看到活动的书《机器学习项目开发实践》&#xff0c;自行就到各大网络商店上购买了书&#xff0c;据反馈这个书很不错&#xff0c;小二昨天也收到一本人民邮电出版社的书…