首页 /  学术天地 / 柯·学堂

【柯·学堂】SAS宏

讯息发布时间:2019-08-19 18:08


一、什么是宏?

当我们刚开始接触宏的时候,往往会使用些由%LET、CALL SYMPUT和PROC SQL INTO等建立一个全局的宏,应用到后续的过程步中,随着这些宏的频繁使用,我们脑海里往往会形成一个这样的概念:在SAS中,预处理器会根据一系列预定义的“内容”对源代码进行替换运行。如下例1:

%LET OUTPATH = %STR(E:\TEST\) ;

%LET TABLENAME = %STR(TEST);
********
ODS RTF FILE = “&OUTPATH.&TABLENAME..rtf” ;

********

但随着宏更进一步的使用,慢慢的会发现自己之前给的这么一个定义也没那么准确,好像差点什么,尤其是在宏过程的使用中。宏参虽然也是一个个“内容”,但更确切的说,它是一个个符合需求的条件,这个时候我们的概念就更加清晰了:在SAS中,预处理器会根据一系列定义的规则有条件地对源代码进行替换运行。如下例2:

LET OUTPATH = %STR(E:\TEST\) ;

%MACRO CLASS(FACTOR ,TABLENAME) ;
DATA CLASS ;
SET SASHELP.CLASS ;
&FACTOR. ;
RUN ;
ODS RTF FILE “&OUTPATH.&TABLENAME..rtf” ;
PROC REPORT DATA = CLASS ; RUN ;
ODS RTF CLOSE ;
%MEND ;
%CLASS(WHERE AGE > 13 ,TABLENAME1);
%CLASS(WHERE AGE > 15 ,TABLENAME2);

二、什么时候使用宏?

我们使用的宏的目的往往是因为它“省事”,使用宏可以让我们少写点代码,让我们代码看起来很简洁,减少手动更改的操作量。基于这些目的我们可以知道我们应当在以下情境下使用宏:

1、一个重复使用的变量,如例1的&OUTPATH;
2、一个重复使用的语句内容,如例2的&FACTOR;
3、一个重复使用的过程步,如例2的DATA STEP;
4、一个重复使用的综合过程,如这个例2;

三、怎么去使用宏?

❑  宏与PDV

我们接下来会通过三个方面来讲解怎么去使用宏,第一个方面是全局宏和局部宏,第二个方面是循环体和条件语句,第三个方面是如何进一步的提升运行效率。但在考虑怎么应用宏之前,我们有必要了解下宏在PDV(Program Data Vector)是怎么被处理的。如下图1所示:

 

我们可以看到,相对一般DATA STEP的PDV过程,拥有宏的程序内会多一个宏处理器的过程。在首次确定宏之后会进行一个预编译,再到了宏展开的时候,会将预编译的结果返回值Buffers进行一次非宏过程过程的编译过程。这过程也很简单,但不能被忽略,比如在DATA STEP中应用CALL EXECUTE,而且在CALL EXECUTE中创建宏变量的同时应用该宏变量,这就犯了这个PDV的过程,要知道CALL EXECUTE里边的内容是先被编译然后再去执行的。

❑  全局宏与局部宏

在应用宏过程中,我们需要知道全局宏变量和局部宏变量的概念,全局宏的定义是在开放型代码中定义的宏为全局宏,而局部宏的定义则为在一个宏过程中定义或生成的宏为局部宏。两者的区别很显然,是一个相对的概念,是否是在宏过程中创建的宏变量。两者都可以通过%LET、CALL SYMPUT语句和PROC SQL过程等语句或过程创建,全局宏不可以转换为局部宏,但局部宏可以通过%GLOBAL转换为全局宏。

我们了解了全局宏和局部宏,还需要知道的是全局宏可以应用到局部宏,一级宏中嵌套了二级宏,这个时候一级宏的宏参可以被应用到二级宏中。值得注意的是,如果一级宏的宏参名字和二级宏参的名字出现了一样的情况,这会出现以下的情况:

虽然对于懒得想名字的人来说,这无疑是个好应用,但如果这样操作,势必会增加Debug的难度。


❑  循环体和条件语句

在宏应用里边,还有个重要的内容我们需要了解的是循环体和条件语句,循环体如下:
1、%DO <MACRO-VAR> = <START> %TO <END> [%BY STEP];
MACRO STATEMENT ;
%END ;
2、%DO %WHILE <MACRO EXPRESSION> ;  
MACRO STATEMENT ;
%END ;
3、%DO %UNTIL <MACRO EXPRESSION> ;
MACRO STATEMENT ;
%END ;

条件语句如下:

1、%IF <MACRO EXPRESSION>  %THEN <TRUE MACRO STATEMENT>;

%ELSE <FALSE MACRO STATEMENT>;

和平常使用的未加百分号的循环体不同的是,这里加了百分号的循环体是可以脱离DATA STEP而存在的,也就是说循环体中间的“MACRO STATEMENT”可以是过程步,也可以是语句。那么怎么去循环一个数据集呢?

以DIXON检验法为例,不同样本量之间的高端离群值、低端离群值、检出水平和剔除水平都是不一致的,每发现出一个新的统计离群值,就要在原来的样本上剔除该样本,对剩下的样本继续检验,直至高端和低端都没有统计离群值出现,才终止循环,同时这个时候需要将统计学离群值呈现出来。如果使用循环体,又该如何操作?

很显然,我们是反复的对同一个数据集进行操作,中止循环的条件是高端和低端同时没有出现统计离群值,在这里会使用到宏嵌套宏的办法掺杂着循环体,如下所示简要代码:

%MACRO DIXON(MACRO PARAMETERS1 ,……);

%DO  %UNTIL(&DIXON_OUTLIERS. =  0 );
%GLOBAL  DIXON_OUTLIERS ;
%DIXON_PROCESS(&DATA._&TARGET._4) ;
%END;

%MEND;

宏过程DIXON中嵌套着宏过程DIXON_PROCESS,同时宏过程中会返回数据集之外还会返回由宏过程DIXON_PROCESS产生的全局宏DIXON_OUTLIERS,用以做循环体%DO %UNTIL的循环判断。


四、进一步提升效率

❑ 从宏过程输出着手
减少中间数据集的生成及陈列,可以使用PROC DATASETS指定性的删除work中的数据集,同时不陈列删除了的数据集名称,这样使用SAS EG或 SAS BASE的时候很快捷的浏览自己所需要的数据集,可以使用 NOLIST 和 NOPRINT选项的尽量使用该选项,这样某些程序产生的结果不必要的生成在HTML页面或SAS结果页面上;
❑ 从宏结构着手
减少宏过程,增加宏过程;比如就简单的区分空腹和餐后,在开头就进行简单判断,只分析餐后的数据或者只分析空腹的数据,如果能在不区分空腹和餐后的前提下分析数据,结果还是区分了空腹和餐后,那就一次性分析完,结果呈现上再区分空腹和餐后输出。
技术支持 英铭科技