以下用SAS宏代码实现Apriori的频繁项集寻找。
1、数据整理: 数据集ap2是整理好的业务数据集,物件(item)按c1-c6重新编号(见数据集it1)
data ap;
input id $ item $;
cards;
t1 牛肉
t1 鸡肉
t1 牛奶
t2 牛肉
t2 奶酪
t3 奶酪
t3 靴子
t4 牛肉
t4 鸡肉
t4 奶酪
t5 牛肉
t5 鸡肉
t5 衣服
t5 奶酪
t5 牛奶
t6 鸡肉
t6 衣服
t6 牛奶
t7 鸡肉
t7 牛奶
t7 衣服
;
run;
%dsort(ap out=it ,item)
data it1;
set it;
item_id+1;
vtem=1;
run;
%mer(ap,it1,item,ap1)
%sort(ap1,id item_id)
proc transpose data=ap1 out=ap2 prefix=c;
by id;
id item_id;
var vtem;
run;
2、初始化。C为候选集系列,F为达到支持度要求的频繁项集。C1,C2 单独整理,C3及之后全部由迭代部分完成。控制部分建立了一个iter表,宏会根据这个表进行迭代。
/*第一次建立C1*/
/*1获取变量列_全部为数字型*/
%let lib=WORK;
%let set=AP2;
proc sql noprint;
select num_numeric into :n1
from sashelp.vtable
where libname="&lib" and memname="&set";
quit;
/*输入C1*/
data c1;
do id=1 to &n1;
item1=cats('c',id);
output;
end;
run;
/*计算F1,使用j2的fset宏*/
%fset(1,ap2,c1,f1);
/*手动生成C2,使用c2的c2宏*/
%c2;
/*生成f2*/
%fset(2,ap2,c2,f2);
/*生成iter表*/
data iter;
length inset $ 10;
iter=3;
inset='f2';
run;
3、迭代。从第三次开始迭代,max为最大可能的频繁项,中途如果不满足迭代条件则会直接退出宏。
/*迭代步*/
%macro apro(start=3,max=10);
/*增加主控步~ iter表*/
%do iter=&start %to &max;
proc sql noprint;
select count(*) into :jud
from iter
where iter=&iter;
quit;
%if &jud eq 0 %then
%do;%goto jumpout;%end;
/*此处加入处理步*/
/*从iter表获取迭代初始数据集*/
data _null_;
set iter;
if iter eq &iter then do;
call symput('inset',inset);
end;
run;
/*迭代1-生成合并集*/
%csetg(&iter,&inset,mset);
%csetp(&iter,mset,&inset,%sysfunc(cats(c,&iter)));
%fset(&iter,ap2,%sysfunc(cats(c,&iter)),%sysfunc(cats(f,&iter)));
%end;
%jumpout: ;
%mend;
%apro;
迭代表:
iter字段 本次迭代次数
inset 本轮使用的F数据集(上一轮产生)
宏:
1、c2。通过f1产生c2,2-频繁候选集通过满足支持度的1-频繁集中产生。
2、fset。根据交易数据和候选数据集生成满足支持度的频繁集。
3、csetg。根据上一轮的F集,合并产生新的候选集。
4、csetp。对新的候选集进行剪枝。
宏代码:
1、c2.
%macro c2;
/*获得C1的记录数*/
data c2;
length item1-item2 $ 8;
run;
data _null_;
if 0 then set c1 nobs=cn;
call symput('cn',cn);
run;
%do i=1 %to &cn-1;
data lf;
set f1;
if _n_=&i;
lf=item1;
keep lf;
run;
%do j=&i+1 %to &cn;
data rt;
set f1;
if _n_=&j;
rt=item1;
keep rt;
run;
data mer;
length item1-item2 $ 8;
set lf;set rt;
item1=lf;
item2=rt;
keep item:;
run;
proc append base=c2 data=mer;
run;
%end;
%end;
data c2;
set c2;
if item1 ne '';
run;
%mend;
2、fset
%macro fset(iter,tset,cset,oset,sup=0.3);
/*1取出交易记录的行数*/
data _null_;
if 0 then set &tset nobs=n;
gate=ceil(n*&sup);
call symput('gate',gate);/*传递到宏变量*/
run;
/*2取出C集记录行数*/
data _null_;
if 0 then set &cset nobs=n;
call symput('cnum',n);
run;
/*3对C记录开始循环*/
%do i=1 %to &cnum;
data vec;
set &cset;
if _n_=&i;/*每次取出一行记录*/
run;
/*提取规则*/
data _null_;
set vec;
rul=catx(' and ',of item1-%sysfunc(cats(item,&iter)));
call symput('rul',rul);/*获得规则参数*/
run;
data trans;
set &tset end=last;
if &rul then cnt+1;
if last then do;
call symput('cnt',cnt);
end;
run;
data &cset;
set &cset;
if _n_=&i then cnt=symget('cnt');
run;
%end;
data &oset;
set &cset;
if cnt ge &gate ;
run;
/*如果iter ge 3且f级中数量ge 2,那么就在iter表中注册新任务*/
proc sql noprint;
select count(*) into :inum
from &oset;
quit;
%if &iter ge 3 and &inum ge 2 %then %do;
data task;
length inset $ 10;
iter=&iter+1;
inset="&oset";
run;
proc append base=iter data=task;
run;
%end;
%mend;
3、csetg
%macro csetg(iter,inset,outset);
%let la=%eval(&iter-1);/*获得k-1的下标*/
/*设置一个临时数据集存放新生成的记录*/
data tem;
array item(*) $ item1-%sysfunc(cats(item,&iter));
run;
/*获取输入数据集的行数*/
data _null_;
if 0 then set &inset nobs=n;
call symput('rnum',n);
run;
%do i=1 %to &rnum-1;/*从第一行开始循环*/
data lf;
set &inset;
if _n_=&i;
array lf(*) $ lf1-%sysfunc(cats(lf,&la));
array it(*) $ item1-%sysfunc(cats(item,&la));
do i=1 to &la;
lf=it;
end;
keep lf:;
run;
%do j=&i+1 %to &rnum;/*匹配第二行到末尾*/
/*获取待合并的行*/
data rt;
set &inset;
if _n_=&j;
array rt(*) $ rt1-%sysfunc(cats(rt,&la));
array it(*) $ item1-%sysfunc(cats(item,&la));
do i=1 to &la;
rt=it;
end;
keep rt:;
run;
/*尝试合并唯有最后一项不同的记录*/
data mer;
set lf;set rt;
array mer(*) $ item1-%sysfunc(cats(item,&iter));
array lf(*) $ lf1-%sysfunc(cats(lf,&la));
array rt(*) $ rt1-%sysfunc(cats(rt,&la));
do i=1 to &la-1;/*比较前面k-2项*/
if lf ne rt then go to jump;/*碰到有不同的就跳过*/
mer=lf;/*否则就进行合并*/
end;
/*最后两项分别取两条记录的最后一项*/
mer[&la]=lf[&la];
mer[&iter]=rt[&la];
jump:call symput('bt',0);/*如果前面k-2有不同的那么不会写入新的合并集*/
if mer[&iter] ne '' then do;
call symput('bt',1);/*如果仅有最后一项不同则准备写入*/
end;
keep item:;
run;
%if &bt %then %do;
proc append base=tem data=mer;
run;
%end;
%end;
%end;
data &outset;
set tem;
if item1 ne '';
run;
%mend;
4、csetp
%macro csetp(iter,inset,chset,outset);
%let la=%eval(&iter-1);
/*获取输入数据集行数*/
data _null_;
if 0 then set &inset nobs=n ;
call symput('rnum',n);
run;
/*获取f(k-1)集*/
data chset;
set &chset;
keyw='a';
keep item: keyw;
run;
%do i=1 %to &rnum;/*从第一行开始*/
data tem;/*获取第一行*/
set &inset;
if _n_=&i;
run;
%let kcnt=0;
%do j=1 %to &iter;/*每次丢弃一项,形成k-1子集*/
data tem1;
set tem;
drop %sysfunc(cats(item,&j));
keep item:;
run;
/*重新建立k-1数组*/
data tem2;
set tem1;
array cmp(*) $ cmp1-%sysfunc(cats(cmp,&la));
array it(*) $ item:;
keyw='a';
do i=1 to &la;
cmp=it;
end;
keep keyw cmp:;
run;
/*合并C集和检验的子集,通过关键字keyw*/
%mer(chset,tem2,keyw,chset1);
data chset2;
set chset1;
array it(*) $ item1-%sysfunc(cats(item,&la));
array cmp(*) $ cmp1-%sysfunc(cats(cmp,&la));
lcnt=0;
do i=1 to &la;
if it eq cmp then lcnt+1;
end;
if lcnt eq &la then res=1;
else res=0;
run;
/*每次搜索应当只匹配到一项*/
proc sql noprint;
select sum(res) into :res
from chset2;
quit;
/*累加匹配次数*/
%let kcnt=%eval(&kcnt+&res);
%end;
data &inset;
set &inset;
if _n_=&i and (&kcnt eq &iter) then chk=1;/*对循环的行标记结果*/
run;
%end;
data &outset;
set &inset;
if chk;
run;
%mend;