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 变量创建标志变量,那么 运行 数据步骤之前的宏。