SAS宏:循环创建变量[宏/引用问题]

时间:2017-01-09 19:08:20

标签: sas sas-macro

基本上我正在编写一个宏,它将把输入表,输出表和变量列表作为参数。我的变量列表显示为单个参数,我使用空格字符作为分隔符。 我的宏应该将我的列表分成nbvar宏变量,它们将包含我的(SAS)变量的名称。然后我使用datastep将我的(SAS)变量从其原始字符格式输入到数值。

这是我的代码:

%macro convert_car_to_num(input,output,listvar);

/* First I split my list into nbvar variables named var&i
%qscan to avoid macro resolution of names, not really necessary here
but still works fine. My delimiter is space character, hence 
%str( ) in the %qscan*/

%let nbvar=%sysfunc(countw(&listvar));
%do i = 1 %to &nbvar;
    %let var&i=%qscan(&listvar,&i,%str( ));
%end;

/*Here is my data step. &&var&i_num is resolved just fine*/
data &output;
set &input;
%do i = 1 %to &nbvar;
    &&var&i.._num = input(&&var&i,BEST16.);
%end;
run;

%mend;

由于&& var& i .._ num和&& var& i已经解决,我希望我的代码能够正常工作,但我的日志显示:

varname_num

180

解析名称" varname"有下划线。我发现后有点:

错误180-322:声明无效或使用不正确。

错误的分号通常是标准错误。但我知道我的宏变量已经解决,因为mprint显示:

MPRINT(CONVERT_CAR_TO_NUM):varname_num = input(varname,BEST16。)

注意:由宏变量生成的行" VAR26"。

MPRINT(CONVERT_CAR_TO_NUM):运行;

其中varname是我列表中第26个变量的正确名称,表示分辨率工作正常。

为了让我更难以理解,我会指出相同的代码:

&&var&i.. = input(&&var&i,BEST16.);

编译,即使它没有达到预期的结果(变量仍然是char)。

同样,代码与:

相同
&&var&i.._num = &&var&i;

也不编译。

我还测试过将我的宏变量的名称更改为num_&& var& i或n&& var& i,或甚至首先声明一个宏变量" name&#34 ;它包含&& var& i,都具有相同的效果。不选择与初始变量相同的名称似乎会导致代码显示180错误。

我想这个问题存在于尝试声明一个变量,知道我编写的前一个和类似的代码片段确实有效,datastep是一个比较(从变量列表中将缺失值转换为零):

data &output;
set &input;
    %do i = 1 %to &nbvar;
        if &&var&i = . then &&var&i = 0;
    %end;
run;

但对于同一段代码,如果我尝试创建一个新变量(同样具有任何名称),写作:

if num_&&var&i = . then &&var&i = 0;

我发现自己的解析名称再次加下划线,但现在指向以下错误:

错误22-322:语法错误,期待以下之一:!,!!,&,(,*,**,+, - ,/,;,<,               < =,<>,=,>,><,> =,AND,EQ,GE,GT,IN,LE,LT,MAX,MIN,NE,NG,NL,NOTIN,或,               [,^ =,{,|,||,〜=。

2 个答案:

答案 0 :(得分:1)

这是SAS无法自动取消引用值的问题。我学到的规则是,如果您的SAS代码(由MPRINT显示)看起来有效,但是您遇到错误,请尝试取消引用。

在您的情况下,请更改为:

%unquote(&&var&i.._num) = input(&&var&i,BEST16.);

使代码有效。当然根据您的评论,您可能不需要%qscan来引入有问题的引用字符。如果您将其更改为%scan,则无需%unquote(),因为它不会被引用。

同意@Foxer的方法,使用单个宏变量将i_th变量存储在列表中。还建议制作这些%局部变量以避免冲突。可能是这样的:

%macro convert_car_to_num(input,output,listvar);
  %local i vari;

  data &output;
    set &input;
    %do i = 1 %to %sysfunc(countw(&listvar,%str( )));
      %let vari=%scan(&listvar,&i,%str( ));
      &vari._num=input(&vari,best16.);
    %end;
  run;

%mend;

答案 1 :(得分:0)

以下是否满足您的需求?我的猜测是你在单独的if-then-do语句中有太多的内容:

%macro new(input,output,listvar);
data &output; set &input;
%do i=1 %to %sysfunc(countw(&listvar.));
    %let var=%scan(&listvar.,&i.);
        var&i._num = input(&var.,BEST16.);
%end;
run;
%mend;

%new(have,want,&listvar.);

如果你正在尝试为你正在循环的每个变量创建一个单独的宏变量,下面也可以做你想要的(虽然在这种情况下它可能不值得,但只是为其他应用程序显示另一个有用的方法):

** put variables into dataset **;
proc sql noprint;
    create table vars
    as select name,type
    from dictionary.columns
    where upcase(libname)="WORK" and 
        upcase(memname)="HAVE" and
        type = "char";
quit; 

** create total count and separate macro variable for each variable **;
data _null_; set vars end=last;
    by name;
    i+1;
    call symputx('name'||strip(put(i,8.)), name);
        if last then call symputx('count',i);
run;

%put &count.;
%put &name1.;
%put &name2.;
%put &name3.;

** loop over each variable using the total count **;
%macro new(input,output);
data &output; set &input;
%do i=1 %to &count.;
    &&name&i.._num = input(&&name&i,BEST16.);
%end;
run;

%mend;

%new(have,want);