SAS Call每次运行时都会执行输出更改

时间:2017-01-05 10:16:37

标签: macros sas execute sas-macro datastep

我在数据步骤中运行一个带调用execute的宏。 我想从数据步骤中得到以下内容:

获取一个表,为每个现有列添加一个新列(通过宏),最后添加一个新列,该列是另外两个列的总和。 我想这也可能没有宏,但我想要这样,因为我是SAS新手,想要了解宏的逻辑并调用execute。

所以让我说我有下表:

data values;
input a1 a2 b1 b2;
datalines;
1 0 3 10
0 5 6 11
7 8 9 0
;
run;

和这个宏:

%macro loop1(myDataset);  
proc contents data=&myDataset. out=Col_Names (keep=Name) noprint;
run;
proc sql noprint; 
select count(Name) into :length from Col_Names;
quit;                                                 
     %do j = 1 %to &length; 
            data &myDataset.;
            set &myDataset.; 
            n&j=0;
            run;                       
     %end;
%mend;

然后以下数据步骤在我运行前三次创建不同的输出: (每次运行后我都会用数据线重新运行原始数据步骤)

data values;
set values;
if _n_=1 then call execute('%loop1(values);');
test=sum(a1,a2);
run;

第一次运行会导致错误:

  

警告:未解析明显的符号引用LENGTH。错误:A   在%EVAL函数或%IF条件中找到了字符操作数   其中需要数字操作数。条件是:          & length ERROR:%DO J循环的%TO值无效。错误:宏LOOP1将停止执行。

第二次运行完全符合我的要求:

a1,a2,b2,b2,test,n1,n2,n3,n4

从第三次开始,输出保持不变:

a1,a2,b2,b2,test,n1,n2,n3,n4,n5

其中包含不受欢迎的 n5

我应该更改以始终从第二次运行获得输出?

3 个答案:

答案 0 :(得分:2)

使用call execute调用宏时,建议按照以下使用说明将其包装在%nrstr()中:

http://support.sas.com/kb/23/134.html

这可以防止过早的宏执行 - 或者至少迫使它等待任何相关的宏变量准备就绪,即length子句中的INTO:变量。

data values;
  set values;
  if _n_=1 then call execute('%nrstr(%loop1(values);)');
  test=sum(a1,a2);
run;

为了获得您想要的结果,您还需要在SQL过程中排除'test'变量,如下所示:

proc sql noprint; 
select count(Name) into :length from Col_Names
  where upcase(name) ne 'TEST';

答案 1 :(得分:1)

为什么会失败:

您认为,您的代码根本不会在'value'数据集中添加变量'n1'等等。它会在您的最后一个数据步骤之后安排您的宏,这不是您想要的。

尝试将作业添加到上一个数据步骤

data values;
    set values;
    *&new_columns;
    if _n_=1 then call execute('%loop1(values);');

    test=sum(a1,a2);
    n2=9874;
run;

你将看到它没有效果,因为当你的宏运行时你的'n2'值会被覆盖;

你能做什么:

使用select <something> into :<variable> separated by <separator>,您可以创建包含分配的宏变量。

proc contents data=values noprint out=Col_Names(keep=varnum);
run;
proc sql noprint;
    select 'n'|| strip(put(varnum, 8.)) ||'=0'
    into :new_columns separated by ';'
    from col_names;
quit;

您可以在数据步骤中使用此变量:

data values;
    set values;
    &new_columns;

    test=sum(a1,a2);
    n2=9874; ** Now this has effect **;
run;

答案 2 :(得分:0)

失败的原因是SAS在数据步骤运行时运行宏并将宏生成的代码推送到堆栈。然后在数据步骤完成后运行实际生成的PROC和DATA步骤。

因此,当您的宏运行时,它会生成PROC SQL步骤,但由于您的数据步骤仍在运行,因此该步骤尚未执行。然后,宏运行%DO循环并生成错误,因为宏变量LENGTH不存在,或者使用数据步骤开始之前存在的宏变量的值。

为了防止在%NRSTR()中包装宏调用,以便在数据步骤停止后将宏调用本身压入堆栈以运行。

call execute('%nrstr(%loop1)(values);');

您的实际示例根本不需要CALL EXECUTE。只需在数据步骤运行后调用宏。

data values;
  set values;
  test=sum(a1,a2);
run;
%loop1(values);

或者,如果您不想为新的TEST变量创建一个标志变量,那么在数据步骤之前运行宏。