SAS 宏编码 [英] SAS Macro coding

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

问题描述

我不明白我的 SAS 代码中发生了什么.代码的行为符合预期,但仅在第一次编译时,即名为SummaryTable"的表显示代码触发 %mylogreg 和 %ModelsSummary 的 3 次迭代中的每一次的正确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 帖子/论文/讨论中学到的.如果你用谷歌搜索Ian Whitlock Call Execute",你会发现比我能提供的更好的解释,但这里有一个镜头.

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 步骤将被执行,并将填充 &list,然后将编译并执行 DATA LIST 步骤.但是请记住,这个宏是由 CALL EXECUTE 调用的,在一个仍在运行的 DATA STEP 内.SAS 无法在执行主 DATA STEP 的同时执行 PROC SQL(忽略较新的 DOSUBL 函数和类似函数).因此,在生成 DATA LIST 代码时,宏变量 &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).

那么为什么宏变量在宏之外解析(返回男性列表)?请注意,宏生成的代码实际上是在宏之外执行的.也就是说,调用执行调用了宏,但生成的代码只是简单地放在输入堆栈上.当它执行时,它是开放代码.所以它会生成一个全局宏变量.请注意,您可以看到 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 不会实际调用宏.但它会生成宏调用,并将宏调用放在输入堆栈上.然后当宏执行时,PROC SQL 步骤将在 DATA LIST 步骤之前执行.

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 仅生成两个宏调用(显示在带有 + 前缀的日志中).它实际上并不执行宏.宏在使用 CALL EXECUTE 的数据步骤之后执行.因此,宏生成的代码按预期执行(PROC SQL 步骤在编译和执行 DATA LIST 步骤之前执行).

%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天全站免登陆