记录近期小改Apriori至MapReduce上的心得
·背景
前一阵,一直在研究一些ML的东东,后来工作关系暂停了一阵。现在继续把剩下一些热门的算法再吃吃透,"无聊+逗比"地把他们搞到MapReduce上。这次选择的入手对象为Apriori,也就是大家俗称的"关联规则挖掘",有别于CF(协同过滤)的正交输出。再俗一点,就是常被人提及的"啤酒+面包"的故事。
·Apriori算法简介
在关联规则挖掘方面,有两项著名的算法:Apriori和FPgrowth。两者各有特点,由于计算量级别的差异,越来越多的人选择了后者。但这并不意味着Apriori就是垃圾。个人的理解,FPgrowth为MP而生,Apriori为容器而生。当单日志量达到5G,10G以上Apriori在计算方面的吃亏逐步显现,即便如此,对于人们对于尽可能减少Apriori扫描次数的优化机制仍然乐此不疲。尤其是作为容器方面的选择,可以极大的减少整个代码的实现过程和增加可读性,同时又能训练你的脑力和对容器的使用创意。
作为单机版本来说,整体Apriori的逻辑过程如上所述,相比"树"和"图"来说要简单许多,但是仍然暗藏不少重复计算的陷阱。
- 基本条件,有交易记录T和自己设定的支持度support。
- 从1维最小元素开始做group by+count(*),得到统计集合
- 按support条件,把不符合条件的统计对象过滤掉,类似oracle having追加条件。
- 把渣过滤完毕后,做完全组合,也就是大家在初中时候学的完全唯一组合,得到频繁项组合。这其中你也可以自己设个条件,认为XXX就是最大的频繁项。
- 以这频繁项为基础,去扫描交易记录T,得到步骤2。以此反复,找到你想要的关联组合。
作为MP版来说,Apriopi如何压榨Map的资源,真是件令人头痛的事情。如上图所述:
- 每个Map DataNode都会得到最大的频繁项。如果整体交易记录构成是完全随机分布,那最后的C的数量会非常集中而且稳定。
- 不幸的是,你无法保证十几,几十G的交易T中的交易习惯,一定是随机均匀分布,如果是正态、泊松、二项式等等。那C中最大项集合数量会猛增。
- 这时候,必须在从Map DataNode中继续过一遍集合,删除干扰度较高的最大频繁项,尽量找到唯一最大频繁。
· 一些心得体会
- 就像拍照一样,要先构图。整段代码在把小孩子哄睡后花了近一周时间才完成。其中6成时间用于数据结构设计,3成时间用来code,1成用来调试。
- 数据结构返工两次,最后剑走偏锋。楼主逗比地用了二维set,网上都了一把,用的人不多,不是那种new出来的不带属性那种,而是set套set,和map套set。极尽c++容器之能事。感觉好的数据结构,至少少写200行代码。代码思路: C[频繁set<set>]-> [Struct/raw_c] -> Map<set,int> -> L -> set<set>
- 如何减少计算量是件恶心的事,把文本入内存需要一次N量计算,扫面一次就是N量。整个Apriorio的计算至少K·N,着实笨重,这还不算不少代码洁癖的爱好,还会反复叠加计算量。
- 附属的是一个单机版的Apriori,MP的就补贴了,思路如上图所述。在生产环境中还需要考虑到hadoop的输出限制和函数返回值形式,需要尽可能使用引用和指针,减少内存交互。
- 官网有提到支持度这个概念,当然大家也可以完全按照自己的意愿做一些改动和设定,条条大道去罗马,基本就这个模式了。
· 单机版 centos 6(2.6)+gcc433
#include <iostream>
#include <sstream>
#include <fstream>
#include <string.h>
#include <vector>
#include <map>
#include <set>
#include <algorithm> using namespace std; typedef struct { //原始集合结构定义
vector<string> ss;
} t_raw_jh; typedef struct { //有效数据/统计集合定义
set<string> ss;
int sup_num;
} t_raw_tj; t_raw_jh raw_c[50]; //原始数据
map<set<string>,int> raw_cnt_base; //原始元素统计集合
t_raw_tj L[10]; //洗出来的单集合组合
//set<set> new_kc; //洗出来的排列对象集,作为下次扫描的催化剂
int raw_num=0; //原始数据计数器
int l_num=0; //统计数据计算器
int raw_msup=2; //频繁项支持度 /* C[频繁set<set>]-> [Struct/raw_c] -> Map<set,int> -> L -> set<set> */ void CountEleBase(string line,const char* delim,int raw_num); //1+pre,把一条元素洗到二维颗粒,同时统计group by count
void CountEleReal(set<set<string> > &cc); //1+real,根据3的催化剂,扫描原始记录,同时统计group by count
int WashEleBase(); //2,把MAP SORT[group by count]洗出大于支持度的对象集
set<set<string> > GetNewKc(int size); //3,把洗出的对象集合,极限排列出所有可能,作为下次扫描的催化剂
void Display(); //显示原始集合、单元素非排序group by count
void ClearTj(); //把统计表给清空
void RunApriori(set<set<string> > &fc); //嵌套跑,当然也可以迭代WHILE跑,入参为频繁项 int main()
{
std::ios::sync_with_stdio(false);
string filename="./ap001.txt";
string line;
ifstream ifs;
ifs.open(filename.c_str()); while(getline(ifs,line))
{
CountEleBase(line,",",raw_num);
raw_num++;
} // Display();
//得到第一组经过支持度过滤的C
set<set<string> > aaa; //频繁项,根据有效支持元素得出的组合
aaa=GetNewKc(WashEleBase()); RunApriori(aaa); } void RunApriori(set<set<string> > &fc)
{
if(l_num==1) return;
CountEleReal(fc);
fc.clear();
fc=GetNewKc(WashEleBase());
RunApriori(fc);
} void CountEleReal(set<set<string> > &cc)
{
//扫描记录集
cout<<"\n-----扫描记录集-----"<<endl;
//迭代器定义好,在该函数内之后会用到
set<set<string> >::iterator ot_it;
set<string>::iterator in_it;
raw_cnt_base.clear(); for(ot_it=cc.begin();ot_it!=cc.end();ot_it++)
{
int map_cc=0;
for(int i=0;i<raw_num;i++)
{
vector<string>::iterator res;
for(res=raw_c[i].ss.begin();res!=raw_c[i].ss.end();res++)
{
cout<<*res<<" ";
}
cout<<"|";
int map_in_cc=0;
for(in_it=(*ot_it).begin();in_it!=(*ot_it).end();in_it++)
{
cout<<*in_it<<" ";
res=find(raw_c[i].ss.begin(),raw_c[i].ss.end(),*in_it);
if(res!=raw_c[i].ss.end()) map_in_cc++;
}
if(map_in_cc==(*ot_it).size()) map_cc++;
cout<<"|"<<map_in_cc<<endl;
}
raw_cnt_base[*ot_it]=map_cc;
cout<<"-->"<<map_cc<<endl;
}
} set<set<string> > GetNewKc(int size)
{
vector<string>::iterator ss_it;
set<string> kk[size];
set<set<string> > new_kc;
int pi=0; set<string>::iterator kk_it;
//做两两极限排列组合
for(int i=0;i<l_num;i++)
{
set<string> tmp=L[i].ss;
for(int j=i+1;j<l_num;j++)
{
for(kk_it=L[j].ss.begin();kk_it!=L[j].ss.end();kk_it++)
{
tmp.insert(*kk_it);
}
new_kc.insert(tmp);
tmp=L[i].ss;
}
} cout<<"有效支持元素 ---> C组合[频繁项]:"<<endl;
set<set<string> >::iterator out_it;
set<string>::iterator in_it;
for(out_it=new_kc.begin();out_it!=new_kc.end();out_it++)
{
for(in_it=(*out_it).begin();in_it!=(*out_it).end();in_it++)
{
cout<<*in_it<<",";
}
cout<<endl;
}
return new_kc;
} int WashEleBase()
{
map<set<string>,int>::iterator raw_cnt_it;
int i=0;
l_num=0;
ClearTj();
for(raw_cnt_it=raw_cnt_base.begin();raw_cnt_it!=raw_cnt_base.end();raw_cnt_it++)
{
if(raw_cnt_it->second>=raw_msup)
{
L[i].ss=raw_cnt_it->first;
L[i++].sup_num=raw_cnt_it->second;
l_num++;
}
} //Display
cout<<"有效支持元素:"<<endl;
set<string>::iterator ss_it;
for(i=0;i<l_num;i++)
{
for(ss_it=L[i].ss.begin();ss_it!=L[i].ss.end();ss_it++)
{
cout<<*ss_it<<",";
}
cout<<"|"<<L[i].sup_num<<endl;
}
return i;
} void CountEleBase(string line,const char* delim,int raw_num)
{
char *p=NULL,*q=NULL;
p=const_cast<char*>(line.c_str());
while(p)
{
q=strsep(&p,",");
set<string> load_ss;
raw_c[raw_num].ss.push_back(q);
load_ss.insert(q); raw_cnt_base[load_ss]++;
}
} void Display()
{
cout<<"----Print Map-------"<<endl;
map<set<string>,int>::iterator raw_cnt_it;
set<string>::iterator ss_it;
for(raw_cnt_it=raw_cnt_base.begin();raw_cnt_it!=raw_cnt_base.end();raw_cnt_it++)
{
for(ss_it=(raw_cnt_it->first).begin();ss_it!=(raw_cnt_it->first).end();ss_it++)
{
cout<<*ss_it<<" ";
}
cout<<":"<<raw_cnt_it->second<<endl;
} //raw_cnt_base.clear(); cout<<"----Print Raw-------"<<endl;
vector<string>::iterator raw_c_it;
for(int i=0;i<raw_num;i++)
{
for(raw_c_it=raw_c[i].ss.begin();raw_c_it!=raw_c[i].ss.end();raw_c_it++)
{
cout<<*raw_c_it<<" ";
}
cout<<endl;
}
} void ClearTj()
{
for(int i=0;i<10;i++)
{
L[i].sup_num=0;
L[i].ss.clear();
} }
· 另
望各位路过的大侠,嘴上留情,手上斧正,看看能否进一步压缩计算空间量~~。^_^。
记录近期小改Apriori至MapReduce上的心得的更多相关文章
- 记录近期小改K-Means至MapReduce上的心得
背景: 在所有聚类算法中KMeans算是表面上最简单的一种,没有过多恼人的古希腊符号公式,没有过分繁杂的公式嵌套.对于一个初学矩阵或者仅有向量概念的非专业人士的来说,不可不畏是一把踹门利器.这个世界上 ...
- 关于GitHub上传没有记录(小绿块不显示的问题)
最近开始使用上github来上传保存自己在学习中所写过的代码,打算将自己每天的成果能有个保存,然后就利用上GitHub这么一个利器. 听说GitHub的那个绿块是用来记录每天的上传记录的,结果我将代码 ...
- wepy小程序实现列表分页上拉加载(1)
使用wepy开发微信小程序商城第一篇:项目初始化 使用wepy开发微信小程序商城第二篇:路由配置和页面结构 列表页效果图: 1.新建列表页 (1)在pages里面新建一个list.wpy文件 初始代码 ...
- Apriori on MapReduce
Apiroi算法在Hadoop MapReduce上的实现 输入格式: 一行为一个Bucket 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 34 36 38 ...
- 记录近期面试题,面试总结 (从css - vue 全面面试题)
记录近期换工作时遇到的面试题和面试题答案 css 部分 盒模型 问题:说一下 css 的盒模型 盒模型分为标准模型和怪异盒模型(IE 盒模型) 标准盒模型:盒模型的宽高只是内容(content)的宽高 ...
- hdu---(4515)小Q系列故事——世界上最遥远的距离(模拟题)
小Q系列故事——世界上最遥远的距离 Time Limit: 500/200 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)T ...
- HDU-4515 小Q系列故事——世界上最遥远的距离
小Q系列故事——世界上最遥远的距离 Time Limit: 500/200 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) ...
- Naive Bayes在mapreduce上的实现(转)
Naive Bayes在mapreduce上的实现 原文地址 http://www.cnblogs.com/sunrye/p/4553732.html Naive Bayes是比较常用的分类器,因为思 ...
- 微信小程序 在canvas画布上划动,页面禁止滑动
要实现微信小程序 在canvas画布上划动,页面禁止滑动,不仅要设置disable-scroll="true",还要要给canvas绑定一个触摸事件才能生效. <canvas ...
随机推荐
- ASP.NET分页正品—分页真
承接上篇博文<ASP.NET真假分页-假分页>:http://blog.csdn.net/u010773667/article/details/38845009,继续解说ASP.NE ...
- Win10使用中的一些问题
闲来无事,怒装Win10.使用上总体来说还是不错的,比Win8好一个档次吧. 不过呢在使用中遇到两个很郁闷的问题.权且几下 1.Win10激活 使用工具:激活工具 2.激活后浏览器被挟持 这让我现在非 ...
- This Android SDK requires Android Developer Toolkit version 22.6.2 or above.
今天,在android SDK升级时间,我遇到上述错误,经过一番努力仍克服. 解决方法:android-sdk-windows\tools\lib中间plugin.prop在文档 plugin.ver ...
- unity3d c# 产生真正的随机数
虽然能够使用Random类来生成随机数.但它是系统时钟种子,因此,有大量的反复产生伪随机数的. 您可以使用RNGCryptoServiceProvider();相对真随机数生成. 由加密服务提供程序( ...
- JAVA中的I/O流以及文件操作
一 JAVA语言中主要通过流来完成IO操作. 流:计算机的输入输出之间流动的数据序列,也是类的对象.java中的流方式就像是建立在数据交换源和目的之间的一条通信路径. 数据源:计算机中的数据源是指可以 ...
- [Android] App在三星某些机子上闪退:"不保留活动"
今天遇到用户反映应用总是闪退. 现象:在MainActivity后,只要进入任何主进程相关的二级界面,都会导致应用闪退(注:不是崩溃引起的,只是闪退) 分析:1.看log日志,退出前有抛出异常,但查看 ...
- 第一pga 畸形消费分析
第一pga 畸形消费分析 os: aix 6 db:10205 ------使用os 命令观察oracle 存消耗情况 #ps gv ...... ...
- The C5 Generic Collection Library for C# and CLI
The C5 Generic Collection Library for C# and CLI https://github.com/sestoft/C5/ The C5 Generic Colle ...
- DeviceIoControl的使用说明
应用程序和驱动程序的通信过程是:应用程序使用CreateFile函数打开设备,然后用DeviceIoControl与驱动程序进行通信,包含读和写两种操作.还能够用ReadFile读数据用WriteFi ...
- Ajax请求访问action推断文件是否存在
action措辞: public ActionForward fileIsExsit(ActionMapping mapping, ActionForm form, HttpServletReques ...