SAS宏编码

时间:2015-01-02 22:39:41

标签: macros sas sas-macro

我不明白我的SAS代码中发生了什么。代码的行为与预期的一样,但仅在第一次编译时,即名为' SummaryTable'显示正确的LogLik'代码触发%mylogreg和%ModelsSummary的3次迭代中的每次迭代的值。但是,如果我重新运行代码,那么它的行为会有所不同,“LogLik' 3次迭代的值是相同的,它等于最后一次迭代的值。所以,在某种程度上变量' MyLogLik'在第一次编译代码时保存了第三次迭代的值。

拳头数据生成步骤:

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;

然后问题是,如果我重新运行代码,那么' LogLik'在' SummaryTable'将全部相同,并且值对应于第三个模型。正如我之前所说的,第一次运行代码时,循环按预期工作,每个模型都有相应的' LogLik'摘要表中的值'。我已经检查过%ModelSummary是否传递了变量' Counter'的正确值。 (这也可以在SQL输出中确认),看起来是正确的。所以,我觉得我需要的是清理“MyLogLik”的价值。主代码完成后或类似的东西。

有人能指出我缺少的东西吗?我在代码中看不出有什么问题!

3 个答案:

答案 0 :(得分:4)

简短回答是尝试将您的宏调用包装在%NRSTR()中。这将是一个很长的解释。但宏观语言很难。当用于调用宏时调用执行的时间使得它更加棘手。以下大部分内容都是我从Ian Whitlock的许多SAS-L帖子/论文/讨论中学到的。如果你google" Ian Whitlock Call Execute"你会找到比我能提供的更好的解释,但这是一个镜头。

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

%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;

这是从调用宏两次(没有调用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.

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

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

%put OUTSIDE MACRO list=&list; 

%*cleanup;
%symdel List;

LOG:

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最终也是作为全局宏变量创建的。令人惊讶的?

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

在上面的示例中,生成PROC SQL步骤,并生成DATA LIST步骤。但是在执行PROC SQL步骤之前生成了DATA LIST步骤!通常,会认为PROC SQL步骤将被执行,并且将填充和列表,然后将编译和执行DATA LIST步骤。但请记住,这个宏是由CALL EXECUTE在仍在运行的DATA STEP中调用的。 SAS无法在执行主DATA STEP的同时执行PROC SQL(忽略较新的DOSUBL功能等)。因此,在生成DATA LIST代码时,尚未填充宏变量& list。它是null。如果宏没有%local语句,我会收到关于宏变量没有解析的警告(正如你所做的那样)。

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

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

此处的代码:

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步骤之前执行)。

关键是当你使用CALL EXECUTE来调用一个宏时,如果那个宏使用DATA STEP或PROC SQL代码来生成宏变量,那么使用%NRSTR()延迟执行是个好主意。宏。

HTH

答案 1 :(得分:0)

它没有跳出什么,确实是错的。你跑的时候会出现任何错误吗?它第一次运行而不是第二次运行这一事实告诉我可能存在的不仅仅是宏变量的范围。

您可以使用MyLogLik%ModelsSummary中声明%local MyLogLik;本地。

答案 2 :(得分:0)

我认为您正在解决调用执行如何工作以及宏变量何时解决的问题。我的理由是,如果您通过%ModelsSummary(..)单独调用宏,则不会发生错误。 我不知道它为什么会发生,希望有人对你有更好的回应。一种解决方法是使用独特的宏变量,即在MyLogLik结束时添加和计数宏变量。

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

   select _LNLIKE_ into :MyLogLik&count from parameters&Count;

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

   drop table parameters&Count;


   quit;
%mend;