数据挖掘Apriori算法报告周一.docx
《数据挖掘Apriori算法报告周一.docx》由会员分享,可在线阅读,更多相关《数据挖掘Apriori算法报告周一.docx(18页珍藏版)》请在冰豆网上搜索。
数据挖掘Apriori算法报告周一
实训报告书
实训名称:
系<部):
专业班级:
学生姓名:
学号:
指导教师:
完成日期:
山东科技大学泰山科技学院
实训课题
实训人姓名
同组人员
实训日期
至
实训成绩
指导教师评语
指导教师签名:
______________
_______年____月____日
数据挖掘Apriori算法报告
一.关联算法简介
关规则数据挖掘是重要的一种数据挖掘方法,它的关键环节是寻找频繁项集.近几年许多数据挖掘领域的研究人员投入了大量的时间和精力,深入研究了关联规则的算法,其中Agrawal等人于1993年提出的Apriori算法就是其中最具代表的成果,随后众多学者又在此基础上提出了一些改进,目的在于提高算法的效率,从而改进数据挖掘的效率.这方面的研究是目前数据挖掘领域研究的热点之一.
1.1 相关概念
所谓关联规则挖掘就是从事务数据库、关系数据库或数据仓库等海量数据的项集之间发现有价值的频繁出现的模式关联和相关性.通过预先设定的支持度和可信度,通过特定的数据挖掘算法获得支持度和可信度均较高的关联规则,得到用户感兴趣、有价值的关联规则并应用到实际工作中,真正实现从数据到信息、再到知识的迁移过程.关联规则数据挖掘的过程大体为两步:
第一步是从全部项集中寻找出所有频繁项集。
第二步是由频繁项集获取关联规则.因为第二步较为容易和直观,所以第一步是关联规则挖掘的核心步骤.目前大多数寻找频繁项集算法因需要大量候选集而效率不高.在关联规则挖掘的方法中,最经典的算法是APriroi算法,除此之外还有基于充分挖掘增量事务的关联规则更新算法、Patition算法、完全频繁项集挖掘、频繁闭项集挖掘、最大频繁项集挖掘算法以及一些新的频繁项集挖掘算法.
1.2 Apriori算法思想
Apriori算法是一种最有影响的挖掘布尔关联规则频繁项集的算法,是Agrawal等人于1993年提出的一种宽度优先算法.算法的核心是使用候选项集找频繁项集。
算法思想如下:
首先扫描数据集中所有事务,统计每个项出现的次数,删除小于预先设定的支持度的项集,得到频繁12项集;利用频繁12项集合的连接,产生候选22项集合(Candi2date22itemset>。
然后对其中每个候选项集的支持计数,得到频繁项集22项集的集合,并利用这些频繁22项集合的连接,得到候选32项集合。
重复扫描数据库产生更高层次的频繁项集合,再连接产生下一级候选项集,直到穷尽数据集中的所有频繁项集。
1.3 Apriori算法的典型改进
Apriori算法能够有效地产生关联规则,但存在算法效率不高的缺陷,因为Apriori算法存在两个比较明显的缺点:
一个是可能产生大量的候选集,另一个是需要重复扫描数据库.因此如果挖掘数据仓库数据量很大,应用此算法每次迭代产生候选项集用来统计其支持度需要花费很多时间。
为了提高算法的效率,国内外专家学者提出的一系列改进算法主要从减少扫描数据库的次数和减少生成候选项目集的数目方面进行优化。
从近几年频繁项集挖掘算法的研究趋势来看,为了提高算法的效率,提出了一系列的混合搜索策略和高效剪枝策略。
当数据集中所包含的项目个数比较多时,马占欣,陆玉昌提出只有恰当地设置2个额外参数,才能够保证挖掘过程的正常进行,但这样做的代价是可能会遗漏部分包含更多负项目的关联规则。
二.关联算法的基本原理
该算法的基本思想是:
首先找出所有的频集,这些项集出现的频繁性至少和预定义的最小支持度一样。
然后由频集产生强关联规则,这些规则必须满足最小支持度和最小可信度。
然后使用第1步找到的频集产生期望的规则,产生只包含集合的项的所有规则,其中每一条规则的右部只有一项,这里采用的是中规则的定义。
一旦这些规则被生成,那么只有那些大于用户给定的最小可信度的规则才被留下来。
为了生成所有频集,使用了递推的方法
<1)L1=find_frequent_1-itemsets(D>。
//挖掘频繁1-项集,比较容易
<2)for(k=2。
Lk-1≠Φ。
k++>{
<3)Ck=apriori_gen(Lk-1,min_sup>。
//调用apriori_gen方法生成候选频繁k-项集
<4)foreachtransactiont∈D{ //扫描事务数据库D
<5)Ct=subset(Ck,t>。
<6)foreachcandidatec∈Ct
<7)c.count++。
// 统计候选频繁k-项集的计数
<8)}
<9)Lk={c∈Ck|c.count≥min_sup}//满足最小支持度的k-项集即为频繁k-项集
<10)}
<11)returnL=∪kLk。
//合并频繁k-项集0)
三.关联算法的C++简单实现
3.1算法数据:
对给定数据集用Apriori算法进行挖掘,找出其中的频繁集并生成关联规则。
对下面数据集进行挖掘:
I1I2I5
I1I2
I2I4
I1I2I4
I1I3
I1I2I3I5
I1I2I3
I2I5
I2I3I4
I3I4
对于数据集,取最小支持度minsup=2,最小置信度minconf=0.8。
3.2算法步骤:
①首先单趟扫描数据集,计算各个一项集的支持度,根据给定的最小支持度闵值,得到一项频繁集L1。
②然后通过连接运算,得到二项候选集,对每个候选集再次扫描数据集,得出每个候选集的支持度,再与最小支持度比较。
得到二项频繁集L2。
③如此进行下去,直到不能连接产生新的候选集为止。
④对于找到的所有频繁集,用规则提取算法进行关联规则的提取。
3.3C++算法的简单实现
①首先要在项目名文件夹里自己定义date.txt文档存放数据,然后在main函数中用FILE*fp=fopen("date.txt","r">。
将数据导入算法。
②定义intcountL1[10]。
找到各一维频繁子集出现的次数。
定义charcurL1[20][2]。
实现出现的一维子集。
因为给出的数据最多有4个数,所以同样的我们要定义到4维来放数据。
intcountL2[10]。
//各二维频繁子集出现的次数
charcurL2[20][3]。
//出现的二维子集
intcountL3[10]。
//各三维频繁子集出现的次数
charcurL3[20][4]。
//出现的三维子集
charcur[50][4]。
③定义intSizeStr(char*m>得到字符串的长度。
实现代码如下:
intSizeStr(char*m>
{
inti=0。
while(*(m+i>!
=0>
{
i++。
}
returni。
}
④比较两个字符串,如果相等返回true,否则返回false
boolOpD(char*x,char*y>
{
inti=0。
if(SizeStr(x>==SizeStr(y>>
{
while(*(x+i>==*(y+i>>
{
i++。
if(*(x+i>==0&&*(y+i>==0>
returntrue。
}
}
returnfalse。
}
⑤通过voidLoadItemL1(char**p>得到所有1元的字串和各自出现的次数
voidLoadItemL1(char**p>
{
inti,j,n=0,k=0。
charch。
char*s。
intf。
memset(cur,0,sizeof(cur>>。
for(i=0。
i<20。
i++>
{
curL1[i][0]=0。
curL1[i][1]=0。
}
for(j=0。
j<10。
j++>
countL1[j]=0。
for(i=0。
i<10。
i++>
for(j=0。
j<4。
j++>
{
ch=*(*(p+i>+j>。
if(ch==0>
break。
cur[n][0]=ch。
n++。
}
curL1[0][0]=cur[0][0]。
curL1[0][1]=cur[0][1]。
k=0。
for(i=0。
i<50。
i++>
{
if(cur[i]==0>
break。
s=cur[i]。
f=1。
for(j=0。
j<=k。
j++>
{
if(OpD(s,curL1[j]>>
{
f=0。
break。
}
}
if(f==1>
{
++k。
curL1[k][0]=cur[i][0]。
curL1[k][1]=cur[i][1]。
}
}
for(i=0。
i<20。
i++>
for(j=0。
j<50。
j++>
{
char*m。
m=curL1[i]。
if(*m==0>
break。
if(OpD(m,cur[j]>>
countL1[i]++。
}
printf("L1:
\n">。
printf("项集支持度计数\n">。
for(i=0。
i<10。
i++>
{
if(curL1[i]==0>
break。
if(countL1[i]>=2>
printf("{I%s}:
%d\n",curL1[i],countL1[i]>。
}
}
⑥通过voidSubItem2(char**p>得到所有的2元子串
voidSubItem2(char**p>
{
inti,j,k,n=0。
char*s。
memset(cur,0,sizeof(cur>>。
for(i=0。
i<20。
i++>
{
curL2[i][0]=0。
curL2[i][1]=0。
curL2[i][2]=0。
}
for(i=0。
i<10。
i++>
countL2[i]=0。
for(k=0。
k<10。
k++>
{
s=*(p+k>。
if(SizeStr(s><2>
continue。
for(i=0。
i。
i++>
for(j=i+1。
j。
j++>
{
if(*(s+j>==0>
break。
*(cur[n]+0>=*(s+i>。
*(cur[n]+1>=*(s+j>。
*(cur[n]+2>=0。
*(cur[n]+3>=0。
n++。
}
}
}
⑦通过voidLoadItemL2(char**p>得到各个2元频繁子串出现的次数
voidLoadItemL2(char**p>
{
intk,i,j。
char*s。
intf。
SubItem2(p>。
curL2[0][0]=cur[0][0]。
curL2[0][1]=cur[0][1]。
curL2[0][2]=cur[0][2]。
k=0。
for(i=0。
i<50。
i++>
{
if(cur[i]==0>
break。
s=cur[i]。
f=1。
for(j=0。
j<=k。
j++>
{
if(OpD(s,curL2[j]>>
{
f=0。
break。
}
}
if(f==1>
{
++k。
curL2[k][0]=cur[i][0]。
curL2[k][1]=cur[i][1]。
curL2[k][2]=cur[i][2]。
}
}
for(i=0。
i<20。
i++>
for(j=0。
j<50。
j++>
{
s=curL2[i]。
if(*s==0>
break。
if(OpD(s,cur[j]>>
countL2[i]++。
}
printf("L2:
\n">。
printf("项集支持度计数\n">。
for(i=0。
i<10。
i++>
{
if(curL2[i]==0>
break。
if(countL2[i]>=2>
printf("{I%c,I%c}:
%d\n",curL2[i][0],curL2[i][1],countL2[i]>。
}
}
⑧通过定义voidSubItem3(char**p>得到所有3元的子串
voidSubItem3(char**p>
{
char*s。
inti,j,h,m。
intn=0。
memset(cur,0,sizeof(cur>>。
for(j=0。
j<20。
j++>
{
curL3[j][0]=0。
curL3[j][1]=0。
curL3[j][2]=0。
curL3[j][3]=0。
}
for(i=0。
i<10。
i++>
countL3[i]=0。
for(m=0。
m<10。
m++>
{
s=*(p+m>。
if(SizeStr(s><3>
continue。
for(i=0。
i。
i++>
for(j=i+1。
j。
j++>
{
for(h=j+1。
h。
h++>
{
if(*(s+h>==0>
break。
*(cur[n]+0>=*(s+i>。
*(cur[n]+1>=*(s+j>。
*(cur[n]+2>=*(s+h>。
*(cur[n]+3>=0。
n++。
}
}
}
}
⑨同样我们要得到得到各个3元频繁子串出现的次数
voidLoadItemL3(char**p>
{
intk,i,j。
char*s。
intf。
SubItem3(p>。
curL3[0][0]=cur[0][0]。
curL3[0][1]=cur[0][1]。
curL3[0][2]=cur[0][2]。
curL3[0][3]=cur[0][3]。
k=0。
for(i=0。
i<50。
i++>
{
if(cur[i]==0>
break。
s=cur[i]。
f=1。
for(j=0。
j<=k。
j++>
{
if(OpD(s,curL3[j]>>
{
f=0。
break。
}
}
if(f==1>
{
++k。
curL3[k][0]=cur[i][0]。
curL3[k][1]=cur[i][1]。
curL3[k][2]=cur[i][2]。
curL3[k][3]=cur[i][3]。
}
}
for(i=0。
i<20。
i++>
for(j=0。
j<50。
j++>
{
s=curL3[i]。
if(*s==0>
break。
if(OpD(s,cur[j]>>
countL3[i]++。
}
printf("L3:
\n">。
printf("项集支持度计数\n">。
for(i=0。
i<10。
i++>
{
if(curL3[i]==0>
break。
if(countL3[i]>=2>
printf("{I%c,I%c,I%c}:
%d\n",curL3[i][0],curL3[i][1],curL3[i][2],countL3[i]>。
}
}
⑩定义voidLoadItemL4(char**p>得到各个3元子串出现的次数
voidLoadItemL4(char**p>
{
inti。
char*s。
intj=0。
for(i=0。
i<10。
i++>
{
s=*(p+i>。
if(SizeStr(s>==4>
j++。
}
printf("四维子集出现的次数:
%d\n",j>。
printf("没有四维的频繁子集,算法结束!
\n">。
}
通过voidSupport(char*w,intg>得到关联规则,并输出结果
voidSupport(char*w,intg>
{
inti,j,k,n=0。
char*s。
floatc=0.8,d=0。
memset(cur,0,sizeof(cur>>。
s=w。
for(i=0。
i。
i++>
{
*(cur[n]+0>=*(s+i>。
*(cur[n]+1>=0。
*(cur[n]+2>=0。
*(cur[n]+3>=0。
n++。
}
for(i=0。
i。
i++>
for(j=i+1。
j。
j++>
{
if(*(s+j>==0>
break。
*(cur[n]+0>=*(s+i>。
*(cur[n]+1>=*(s+j>。
*(cur[n]+2>=0。
*(cur[n]+3>=0。
n++。
}
for(i=0。
i<10。
i++>
{
if(SizeStr(cur[i]>==1>
{
for(j=0。
j<10。
j++>
{
if(OpD(cur[i],curL1[j]>>
{
d=countL3[g]/(float>countL1[j]。
if(d>=c>
printf("{I%s}:
%f",curL1[i],d>。
break。
}
}
}
if(SizeStr(cur[i]>==2>
{
for(j=0。
j<10。
j++>
{
if(OpD(cur[i],curL2[j]>>
{
d=countL3[g]/(float>countL2[j]。
if(d>=c>
printf("{I%c,I%c}:
%f\n",curL2[j][0],curL2[j][1],d>。
break。
}
}
}
}
}
最后通过main函数完成整过程序
intmain(intargc,char*argv[]>
{
inti=0,j=0,k。
charbuf[10][6]。
char*p[10]。
charch。
memset(buf,0,sizeof(buf>>。
FILE*fp=fopen("date.txt","r">。
if(fp==NULL>
return0。
ch=fgetc(fp>。
while(ch!
=EOF>
{
if(ch==0xa||ch==0xd>
{
i++。
ch=fgetc(fp>。
j=0。
continue。
}
buf[i][j]=ch。
j++。
ch=fgetc(fp>。
}
for(k=0。
k<10。
k++>
{
*(p+k>=buf[k]。
}
LoadItemL1(p>。
LoadItemL2(p>。
LoadItemL3(p>。
LoadItemL4(p>。
printf("产生关联规则:
\n">。
printf("非空子集:
置信度:
\n">。
for(i=0。
i<10。
i++>
{
if(curL3[i]!
=0&&countL3[i]>=2>
Support(curL3[i],i>。
}
return0。
}
3.4程序输出结果:
四.学习心得体会
关联算法基本原理学习思路简单,只需一步一步找出频集。
再通过支持度算出可信度,如对于A—>C,support=support<{A,C}),confidence=support<{A,C})/support<{A})。
其中频繁子集的任何子集一定是频繁的,子集频繁父亲一定频繁。
相对较难的的部分在于C++代码的实现部分依次实现各个频繁子集,完成整个程序。
这学期在商务智能课中学习了数据挖掘的10大算法,数据挖掘十分经典,掌握好一个简单的算法要下很功夫,在今后的工作中一定可以用到,我很庆幸这学期能选到这个课,在这之前听说过数据挖掘但一直没有机会接触,原理容易理解但没接触到以前就没有想过这样考虑问题,学习这个课程以后无论是知识面,智力上都是一个跳跃。
还有也提醒我要多复习C++问题的思考,这学期一直忙于网页编程,忘记了上学期说要复习C++数据结构的学习。
希望以后还有机会能选到老师的课。
申明:
所有资料为本人收集整理,仅限个人学习使用,勿做商业用途。