我在数据步骤中运行一个带调用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 。
我应该更改以始终从第二次运行获得输出?
答案 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变量创建一个标志变量,那么在数据步骤之前运行宏。