SAS 宏:循环创建变量 [macro/quoting 问题]
SAS macro: loop to create variables [macro/quoting problem]
基本上我正在编写一个宏,它将我的输入 table、输出 table 和变量列表作为参数。我的变量列表显示为单个参数,我使用 space 字符作为分隔符。
我的宏应该将我的列表分成 nbvar 宏变量,这些变量将包含我的 (SAS) 变量的名称。然后,我使用数据步将我的 (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 = 输入(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,或者甚至首先声明一个包含 &&var&i 的宏变量 "name",所有这些都具有相同的效果。没有选择与初始变量相同的名称似乎会导致代码显示 180 错误。
我想问题在于尝试声明一个变量,知道我之前写的一段类似的代码确实有效,数据步是一个比较(也将变量列表中的缺失值转换为零):
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, OR,
[ ^=, {, |, ||, ~=.
以下是否满足您的需求?我的猜测是你在单独的 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);
这是 SAS 无法自动取消引用值的问题。我学到的规则是,如果您的 SAS 代码(由 MPRINT 显示)看起来有效,但您遇到错误,请尝试取消引用。
在你的情况下,更改为:
%unquote(&&var&i.._num) = input(&&var&i,BEST16.);
使代码工作。当然,根据您的评论,您可能不需要 %qscan
这会引入有问题的引号字符。如果将其更改为 %scan
,则不需要 %unquote()
,因为它一开始就不会被引用。
也同意@Foxer 使用单个宏变量将 i_th 变量存储在列表中的方法。还建议制作这些 %local 变量以避免冲突。可能是这样的:
%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;
基本上我正在编写一个宏,它将我的输入 table、输出 table 和变量列表作为参数。我的变量列表显示为单个参数,我使用 space 字符作为分隔符。 我的宏应该将我的列表分成 nbvar 宏变量,这些变量将包含我的 (SAS) 变量的名称。然后,我使用数据步将我的 (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 = 输入(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,或者甚至首先声明一个包含 &&var&i 的宏变量 "name",所有这些都具有相同的效果。没有选择与初始变量相同的名称似乎会导致代码显示 180 错误。
我想问题在于尝试声明一个变量,知道我之前写的一段类似的代码确实有效,数据步是一个比较(也将变量列表中的缺失值转换为零):
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, OR, [ ^=, {, |, ||, ~=.
以下是否满足您的需求?我的猜测是你在单独的 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);
这是 SAS 无法自动取消引用值的问题。我学到的规则是,如果您的 SAS 代码(由 MPRINT 显示)看起来有效,但您遇到错误,请尝试取消引用。
在你的情况下,更改为:
%unquote(&&var&i.._num) = input(&&var&i,BEST16.);
使代码工作。当然,根据您的评论,您可能不需要 %qscan
这会引入有问题的引号字符。如果将其更改为 %scan
,则不需要 %unquote()
,因为它一开始就不会被引用。
也同意@Foxer 使用单个宏变量将 i_th 变量存储在列表中的方法。还建议制作这些 %local 变量以避免冲突。可能是这样的:
%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;