SAS Call execute output changes every time I 运行 它
SAS Call execute output changes every time I run it
我正在 运行 在数据步骤中调用一个宏来执行。
我想从我的数据步骤中得到的是:
采取 table,为每个现有列添加一个新列(通过宏),最后添加一个新列,该列是其他两个列的总和。
我想没有宏也是可能的,但我想要这样,因为我是 SAS 的新手,想了解宏的逻辑并调用 execute。
假设我有以下 table:
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;
第一个 运行 导致错误:
WARNING: Apparent symbolic reference LENGTH not resolved. ERROR: A
character operand was found in the %EVAL function or %IF condition
where a numeric operand is required. The condition was:
&length ERROR: The %TO value of the %DO J loop is invalid. ERROR: The macro LOOP1 will stop executing.
第二个 运行 结果正是我想要的:
列a1,a2,b2,b2,test,n1,n2,n3,n4
从第三个 运行 开始,输出保持:
列a1,a2,b2,b2,test,n1,n2,n3,n4,n5
里面有不需要的n5。
我应该更改什么才能始终从第二个 运行 获得输出?
使用 call execute
调用宏时,建议按照以下用法说明将它们包装在 %nrstr()
中:
http://support.sas.com/kb/23/134.html
这可以防止过早执行宏 - 或者至少,强制它等待任何相关的宏变量准备就绪,即 INTO:
子句中的 length
变量。
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';
失败原因:
您的代码根本没有在您认为可以的时候将变量“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;
它失败的原因是 SAS 在您的数据步骤 运行 时 运行 启用宏,并将宏生成的代码推入堆栈。然后在数据步骤完成后 运行s 实际生成的 PROC 和 DATA 步骤。
因此,当您的宏 运行s 生成 PROC SQL 步骤时,该步骤尚未执行,因为您的数据步骤仍在 运行ning。然后宏 运行 进入 %DO
循环,并且由于宏变量 LENGTH
不存在而将生成错误,或者使用数据步骤开始之前存在的宏变量的值.
为防止这种情况,将宏调用包装在 %NRSTR()
中,以便在数据步骤停止后将宏调用本身推入堆栈 运行。
call execute('%nrstr(%loop1)(values);');
您的实际示例根本不需要 CALL EXECUTE。只需在数据步骤 运行s.
之后调用宏
data values;
set values;
test=sum(a1,a2);
run;
%loop1(values);
或者,如果您不想为新的 TEST 变量创建标志变量,那么 运行 数据步骤之前的宏。
我正在 运行 在数据步骤中调用一个宏来执行。 我想从我的数据步骤中得到的是:
采取 table,为每个现有列添加一个新列(通过宏),最后添加一个新列,该列是其他两个列的总和。 我想没有宏也是可能的,但我想要这样,因为我是 SAS 的新手,想了解宏的逻辑并调用 execute。
假设我有以下 table:
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;
第一个 运行 导致错误:
WARNING: Apparent symbolic reference LENGTH not resolved. ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: &length ERROR: The %TO value of the %DO J loop is invalid. ERROR: The macro LOOP1 will stop executing.
第二个 运行 结果正是我想要的:
列a1,a2,b2,b2,test,n1,n2,n3,n4
从第三个 运行 开始,输出保持:
列a1,a2,b2,b2,test,n1,n2,n3,n4,n5
里面有不需要的n5。
我应该更改什么才能始终从第二个 运行 获得输出?
使用 call execute
调用宏时,建议按照以下用法说明将它们包装在 %nrstr()
中:
http://support.sas.com/kb/23/134.html
这可以防止过早执行宏 - 或者至少,强制它等待任何相关的宏变量准备就绪,即 INTO:
子句中的 length
变量。
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';
失败原因:
您的代码根本没有在您认为可以的时候将变量“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;
它失败的原因是 SAS 在您的数据步骤 运行 时 运行 启用宏,并将宏生成的代码推入堆栈。然后在数据步骤完成后 运行s 实际生成的 PROC 和 DATA 步骤。
因此,当您的宏 运行s 生成 PROC SQL 步骤时,该步骤尚未执行,因为您的数据步骤仍在 运行ning。然后宏 运行 进入 %DO
循环,并且由于宏变量 LENGTH
不存在而将生成错误,或者使用数据步骤开始之前存在的宏变量的值.
为防止这种情况,将宏调用包装在 %NRSTR()
中,以便在数据步骤停止后将宏调用本身推入堆栈 运行。
call execute('%nrstr(%loop1)(values);');
您的实际示例根本不需要 CALL EXECUTE。只需在数据步骤 运行s.
之后调用宏data values;
set values;
test=sum(a1,a2);
run;
%loop1(values);
或者,如果您不想为新的 TEST 变量创建标志变量,那么 运行 数据步骤之前的宏。