用数据集的方式进行apriori效率实在太低,使用矩阵的方式速度快很多。不过对于有多个高度相关的项还是会无法处理(产生海量的冗余组合),以下是假设有40个冗余变量的情况(这些变量同时出现),apriori在搜索频繁项的时候就会生成数千亿条冗余组合,这个问题是要在apriori之外搞的。
总而言之,用矩阵的方式来实现apriori速度非常快,就是在跑之前要尽量的过滤掉冗余变量。
以下为代码:
ins为输入数据集名称,dim为数字变量的个数,以c1--cn的顺序命名
%macro ap2(ins,dim);
/*1数据集载入矩阵,默认矩阵为m1*/
%im1(&ins,&dim)
/*2执行第一次,并生成一些关键矩阵*/
%im2
/*3执行第二次,生成二项集,并且给出iter_jud来控制循环*/
%im3;
%do iter=3 %to &dim;
%if %eval(&iter-&iter_jud) > 1 %then %goto jumpout;
%im4;
%end;
%jumpout:;
%mend;
%ap2(&ins,&dim)
对应于每个宏:
im1:
%macro im1(ins,vdim);
data m1;
retain c1-c&vdim.;
set &ins;
run;
proc iml ;
use m1;
read all var _num_ into m1;
close m1;
store m1;
quit;
%mend;
im2:
%macro im2;
proc iml;
load m1;
mdim=ncol(m1);
/*支持频数*/
sup=int(nrow(m1)*0.5);
/*支持向量*/
jud=j(1,mdim,sup);
/*全向量 c1*/
vec=j(1,mdim,1);
/*空向量*/
vnull=j(1,mdim,0);
res=vnull;
/*开始计算*/
cal1=m1 & vec;
cal2=cal1[+,];
cal3=cal2 > jud;
/*将f1从cal3从拆解成矩阵*/
do i=1 to mdim;
vtem=vnull;
if cal3
> 0 then do;
vtem=1;
res=res//vtem;
end;
end;
side=res[,+];
res=res[loc(side),];
/*为f1矩阵生成‘侧边栏’*/
side=res[,+];
store sup vnull res side;
quit;
%mend;
im3:
%macro im3;
proc iml;
load m1 sup vnull res side;
/*获取项集=1的位置*/
loc=loc(side=1);
/*取出后组成一行数维数即为个数*/
nloc=ncol(loc);
/*嵌套循环生成候选二项集,先存在候选集里*/
cand=vnull;
do i=1 to nloc-1;
do j=i+1 to nloc;
/*二项集只要按顺序相加即可生成*/
vtem=res[i,]+res[j,];
cand=cand//vtem;
end;
end;
/*对cand进行频数验证*/
/*cand第一项全0不会通过*/
do i=1 to nrow(cand);
t1=m1 & cand[i,];
t1_sum=t1[+,];
tmax=t1_sum[<>];
if tmax > sup then res=res//cand[i,];
end;
side=res[,+];
iter_jud=side[<>];
call symput('iter_jud',char(iter_jud));
store res side;
quit;
%mend;
im4:
%macro im4;
proc iml;
load m1 sup vnull res side;
iter_c=symget('iter');
iter=num(iter_c);
iter_1=iter-1;
/*取k-1项集,即 iter-1*/
loc=loc(side=iter_1);
nloc=ncol(loc);
/*1 growth*/
/*嵌套循环形成全组合*/
cand=vnull;
cand1=cand;
do i=1 to nloc-1;
do j=i+1 to nloc;
/*左边部分*/
lf=res[loc,];
loclf=loc(lf);
lflast=loclf[<>];
lftem=lf;
lftem[lflast]=0;
/*右边部分*/
rt=res[loc[j],];
locrt=loc(rt);
rtlast=locrt[<>];
rttem=rt;
rttem[rtlast]=0;
/*判断部分*/
if lftem = rttem then do;
mer=lf+rt;
mer=mer > 0;
cand=cand//mer;
end;
end;
end;
/*这里应该生成完整的cand,必须要把cand首项移除,否则空值无法继续*/
/*计算侧边栏*/
side_tem=cand[,+];
/*去除第一行空值*/
cand=cand[loc(side_tem),];
/*计算真实的侧边栏*/
side_tem=cand[,+];
/*计算侧边栏的项*/
nrow=nrow(side_tem);
/*2 剪枝*/
/*获取iter-1项集*/
/*获取iter-1项集下标,利用侧边栏*/
loccm=loc(side=iter_1);
cm=res[loccm,];
/*对cand中每一项循环*/
do i=1 to nrow(cand);
check=cand[i,];
ck=0;
/*获取检查向量的1下标*/
loc_tem=loc(check);
/*每项循环k次进行检查*/
do j=1 to iter;
tem=check;
tem[loc_tem[j]]=0;
tem_a=cm & tem;
tem_b=tem_a[,+];
tem_c=tem_b[<>];
/*如果检查数正确那么计数加1*/
if tem_c = iter_1 then ck=ck+1;
else goto mm;
/*如果k项检查都通过,那么把这项附加到cand1中*/
if ck=iter then cand1=cand1//check;
end;
mm:;
end;
/*3再次扫描进行频繁项检查*/
do i=1 to nrow(cand1);
t1=m1 & cand1[i,];
t1_sum=t1[+,];
tmax=t1_sum[<>];
if tmax > sup then res=res//cand1[i,];
end;
side=res[,+];
iter_jud=side[<>];
call symput('iter_jud',char(iter_jud));
store res side;
quit;
%mend;