SAS宏编码 [英] SAS Macro coding

查看:92
本文介绍了SAS宏编码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不理解SAS代码中发生了什么.该代码的行为符合预期,但仅在其首次编译时,即,名为"SummaryTable"的表在代码触发%mylogreg和%ModelsSummary的三个迭代中均显示正确的"LogLik"值.但是,如果我重新运行代码,则其行为会有所不同,这3次迭代的"LogLik"值相同,并且等于上一次迭代的值.因此,以某种方式,变量"MyLogLik"在第一次编译代码时保存了第三次迭代的值.

I dont understand what is going on in my SAS code. The code behaves as expected, but only during the first time it compiles, i.e. the table called 'SummaryTable' shows the correct 'LogLik' value for each of the 3 iterations where the code triggers %mylogreg and %ModelsSummary. However, if I re-run the code then it behaves differently, the 'LogLik' value for the 3 iterations is the same and it is equal to the value for the last iteration. So, in some way the variable 'MyLogLik' saved the value of the third iteration during the first time the code is compiled.

拳头数据生成步骤:

data ingots;
input heat soak r n @@;
datalines;
7 1.0 0 10   7 1.7 0 17   7 2.2 0  7   7 2.8 0 12   7 4.0 0  9
14 1.0 0 31  14 1.7 0 43  14 2.2 2 33  14 2.8 0 31  14 4.0 0 19
27 1.0 1 56  27 1.7 4 44  27 2.2 0 21  27 2.8 1 22  27 4.0 1 16
51 1.0 3 13  51 1.7 0  1  51 2.2 0  1  51 2.8 0  1
;
run;

然后宏:

%macro mylogreg(Num,param);

   title "variables are &param";
   proc logistic data=ingots des outest = parameters&Num noprint;
model r/n = &param;
run;

%mend;

%macro ModelsSummary(Count,Var,Param);
   proc sql;

   select _LNLIKE_ into:MyLogLik from parameters&Count;

   insert into SummaryTable (ModelNumber,ModelVariables,NumParameters, LogLik) values (&Count,"&Var",&Param, &MyLogLik);

   drop table parameters&Count;


   quit;
%mend;

然后输入我的代码

proc sql;
   create table SummaryTable(
                      ModelNumber num,
                      ModelVariables varchar (500),
                      NumParameters num,
                      LogLik num
                      )
                                                           ;
quit;

data _NULL_;

 array a[2] $ (' heat' ' soak');
 length temp $500;

  /*Number of Variables*/
  n = dim(a);

  /*k tell us the number of variables in aech group of all possible conbination*/
  do k=1 to n;

/*total tells the number of different convinations given that we form groups of k variables*/ 
total = comb(n,k);

do j=1 to total;

    /*allcomb is the SAS function which forms all different combinations*/ 
    call allcomb(j,k,of a[*]);
    /*This counter show the total number of convinations for all ks*/
    Counter + 1;

    do i = 1 to k;
        if i = 1 then temp = '';
        /*this temp string contains the explanatory variables to be passed to the logistic reg*/
        temp=catt(temp,a[i]);
    end;

    /* NumParam shows the number of parameters in the logistic model, including the intercept*/
    NumParam = k + 1;

    /* First we call the logistic regression macro, and the we fill out the table summarizing the important results*/
    call execute('%mylogreg('||Counter||','||temp||')');
    call execute('%ModelsSummary('||Counter||','||temp||','||NumParam||')');


  end;

 end;

run;

然后的问题是,如果我重新运行代码,则"SummaryTable"中"LogLik"的值将全部相同,并且该值对应于第三个模型.就像我之前说的那样,我第一次运行代码时,循环按预期工作,并且每个模型在"SummaryTable"中都有其对应的"LogLik"值.我检查了%ModelSummary是否为变量'Counter'传递了正确的值(这也可以在SQL输出中确认),而且看起来是正确的.因此,我相信我需要做的是在主代码完成或类似的操作后清除"MyLogLik"的值.

Then the problem is that if I re-run the code, then the values for 'LogLik' in 'SummaryTable' will all be the same, and the value corresponds to the third model. As I said it before, the first time i run the code, the loop works as expected and each model has its corresponding 'LogLik' value in 'SummaryTable'. I have checked that %ModelSummary is getting passed the correct values for the variable 'Counter' (this can also be confirmed in the SQL output), and it seems correct. So, I beleived that what I need is to clean the value of 'MyLogLik' after the main code is finished or something like that.

有人可以指出我所缺少的内容吗?我看不出我的代码有什么问题!

Can someone point me out what I am missing? I dont see what is wrong in my code!

推荐答案

简短的答案是尝试将宏调用包装在%NRSTR()中.这将是一个很长的解释.但是宏语言很难.当用于调用宏时,调用执行的时间变得更加棘手.以下大部分内容是我从Ian Whitlock的许多SAS-L帖子/论文/讨论中学到的.如果您用Google搜索伊恩·惠特洛克(Ian Whitlock)呼叫执行",您会发现比我能提供的更好的解释,但这是一个尝试.

Short answer is try wrapping your macro call inside %NRSTR(). This will be a long explanation. But the macro language is hard. And the timing of call execute when used to call macros makes it trickier. Most of below is what I learned from many SAS-L posts/papers/discussions by Ian Whitlock. If you google "Ian Whitlock Call Execute" you will find much better explanations than I can provide, but here is a shot.

这是一个有效的简单宏的示例.它使用数据步骤PUT语句将名称列表写入日志. PROC SQL用于生成名称列表并将其存储在宏变量& list中.请注意,该宏变量声明为该宏的本地变量.

Here is an example of a simple macro that works. It uses data step PUT statements to write a list of names to the log. PROC SQL is used to generate the list of names and store it in a macro variable &list. Note that the macro variable is declared to be local to the macro.

%macro ListNames(sex=F);
   %local list;
   proc sql noprint;
     select name into :List separated by " "
     from sashelp.class
     where sex="&sex"
   ;
   quit;

   data list;
     list="&list";
     put "Inside Macro " list=;
   run;

%mend ListNames;

这是两次调用宏(不执行调用)的日志:

Here is the log from calling the macro twice (without call execute):

20         %listNames(sex=F)
Inside Macro list=Alice Barbara Carol Jane Janet Joyce Judy Louise Mary
NOTE: The data set WORK.LIST has 1 observations and 1 variables.
21         %listNames(sex=M)
Inside Macro list=Alfred Henry James Jeffrey John Philip Robert Ronald Thomas William
NOTE: The data set WORK.LIST has 1 observations and 1 variables.

以下是使用不起作用的调用执行两次调用宏的示例.类似于您的代码无法正常工作的方式.首先是代码,然后是日志,然后是我的解释:

Here is an example of calling the macro twice using call execute that does not work. Similar to the way your code does not work. First is the code, then the log, then my explanation:

data _null_;
  input sex $1;
  call execute('%ListNames(sex='||sex||')');
  cards;
F
M
;
run;

%put OUTSIDE MACRO list=&list; 

%*cleanup;
%symdel List;

日志:

30         data _null_;
31           input sex $1;
32           call execute('%ListNames(sex='||sex||')');
33           cards;
NOTE: CALL EXECUTE generated line.
36         ;
1         + proc sql noprint;
1         +                        select name into :List separated by " "      from 
sashelp.class      where sex="F"    ;
1         +
    quit;  
1         +
data list;      list="";      put "Inside Macro " list=;    run;

Inside Macro list= 
NOTE: The data set WORK.LIST has 1 observations and 1 variables.

2         + proc sql noprint;
2         +                        select name into :List separated by " "      from 
sashelp.class      where sex="M"    ;
2         +
    quit;  

2         +
data list;      list="";      put "Inside Macro " list=;    run;

Inside Macro list= 
NOTE: The data set WORK.LIST has 1 observations and 1 variables.

37         run;
38         %put OUTSIDE MACRO list=&list;
OUTSIDE MACRO list=Alfred Henry James Jeffrey John Philip Robert Ronald Thomas William
39         

说明:

请注意,日志中没有警告或错误,但是代码没有起作用".宏中& list的值始终为空.有趣的是,即使宏将& list声明为本地,但& list最终还是作为全局宏变量创建的.令人惊讶吗?

Notice that there are no warnings or errors in the log, yet the code did not "work". The value of &list inside the macro is always empty. And interestingly, even though the macro declares &list to be local, &list is ultimately created as a global macro variable. Surprising?

调用执行和宏语言的工作是生成代码.当您使用call execute调用宏时(如上所述),该宏将执行并生成任何SAS代码,并将其转储到输入堆栈中. 所有代码均在执行任何代码之前生成.

The job of call execute, and the macro language, is to generate code. When you use call execute to invoke a macro (as above), the macro executes and generates whatever SAS code, and dumps it on the input stack. All of code is generated before any of the code is executed.

在上面的示例中,生成了PROC SQL步骤,并且生成了DATA LIST步骤.但是,DATA LIST步骤是在执行PROC SQL步骤之前生成的!通常,会认为将执行PROC SQL步骤,并填充&am​​p; list,然后将编译并执行DATA LIST步骤.但是请记住,此宏是由仍在运行的DATA STEP内部的CALL EXECUTE调用的. SAS无法在执行主要DATA STEP的同时执行PROC SQL(忽略较新的DOSUBL函数及类似功能).因此,在生成数据列表代码时,尚未填充宏变量& list.它为空.如果宏没有%local语句,我将收到有关宏变量无法解析的警告(如您所做的那样).

In the example above, the PROC SQL step is generated, and the DATA LIST step is generated. But the DATA LIST step is generated before the PROC SQL step has executed! Normally, would think the PROC SQL step would be executed, and would populate &list, and then the DATA LIST step would be compiled and executed. But remember that this macro was invoked by CALL EXECUTE, inside of a DATA STEP that is still running. SAS cannot execute PROC SQL at the same time as the main DATA STEP is executing (ignoring newer DOSUBL function and similar). So at the time the DATA LIST code is generated, the macro variable &list has not been populated. It is null. If the macro did not have a %local statement, I would get a warning about macro variable not resolving (as you did).

那么为什么宏变量在宏(返回男性列表)之外解析呢?请注意,由宏生成的代码实际上是在宏之外执行的.也就是说,调用execute调用了宏,但是生成的代码只是放在了输入堆栈上.当它执行时,它处于开放代码中.因此,它会生成一个全局宏变量.请注意,您可以看到CALL EXECUTE生成的所有代码,因为它在日志中以+开头.

So why does the macro variable resolve outside of the macro (returning list of males)? Note that the code generated by the macro is actually executed outside of the macro. That is, call execute invoked the macro, but the code generated is simply put on the input stack. When it executes, it is in open code. So it generates a global macro variable. Note you can see all the code generated by CALL EXECUTE as it is prefaced with + in the log.

解决方案是将宏触发器包装在%NRSTR()中.执行此操作时,调用execute实际上不会调用该宏.但是它将生成宏调用,并将宏调用放在输入堆栈上.然后,在执行宏时,将在DATA LIST步骤之前执行PROC SQL步骤.

Solution is to wrap the macro trigger in %NRSTR(). When you do this, call execute will not actually invoke the macro. But it will generate the macro call, and put the macro call on the input stack. Then when the macro executes, the PROC SQL step will be executed before the DATA LIST step.

这里的代码:

data _null_;
  input sex $1;
  call execute('%nrstr(%%)ListNames(sex='||sex||')');
  cards;
F
M
;
run;

和日志:

49         data _null_;
50           input sex $1;
51           call execute('%nrstr(%%)ListNames(sex='||sex||')');
52           cards;

NOTE: CALL EXECUTE generated line.
55         ;
1         + %ListNames(sex=F)

Inside Macro list=Alice Barbara Carol Jane Janet Joyce Judy Louise Mary
NOTE: The data set WORK.LIST has 1 observations and 1 variables.

2         + %ListNames(sex=M)

Inside Macro list=Alfred Henry James Jeffrey John Philip Robert Ronald Thomas William
NOTE: The data set WORK.LIST has 1 observations and 1 variables.

%NRSTR()用于在CALL EXECUTE中隐藏宏触发器.请注意,调用执行仅生成两个宏调用(在日志中显示为+前缀).它实际上并不执行宏.宏在使用CALL EXECUTE的数据步骤之后执行.因此,由宏生成的代码可以按希望的方式执行(在编译和执行DATA LIST步骤之前执行PROC SQL步骤).

%NRSTR() is used to hide the macro trigger from CALL EXECUTE. Note that call execute only generates the two macro calls (shown in log with + prefix). It does not actually execute the macros. The macros are executed after the data step that used CALL EXECUTE. So the code generated by the macros executes as one would hope (the PROC SQL step is executed before the DATA LIST step is compiled and executed).

关键点在于,当您使用CALL EXECUTE调用宏时,如果该宏使用DATA STEP或PROC SQL代码生成宏变量,则最好使用%NRSTR()来延迟宏的执行.

Key point is that when you use CALL EXECUTE to invoke a macro, if that macro uses DATA STEP or PROC SQL code to generate macro variables, it's a good idea to use %NRSTR() to delay the execution of the macro.

HTH

这篇关于SAS宏编码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆