将等号传递给SAS中的宏

Passing an equals sign to a macro in SAS

我正在尝试在数据步骤中生成代码,然后将代码传递到宏中,然后宏会运行代码。这有点迂回,我知道,但我想不出更好的解决方案,因为我的代码内容基于数据集中的内容。

在我的数据集 "test2" 中,我只有一个变量 "statement" 的观测值,它等于

j1=input(j,anydtdtm.); drop j; rename j1=j; k1=input(k,anydtdtm.); drop k; rename k1=k; l1=input(l,anydtdtm.); drop l; rename l1=l;

我有一个宏,基本上是

%macro dummy(ds,statements);
    data &ds.2;
        set &ds.;    
        &statements.
    run;
%mend;

然后我使用 call execute 执行以下操作:

data test3;
    set test2;
    call execute('%dummy('||strip(ds)||','||strip(statement)||')');
run;

但是我得到以下错误:

ERROR: The keyword parameter J1 was not defined with the macro.

很明显,SAS 将我的“=”符号解释为宏变量内容之外的其他内容。我尝试使用 %str 并将我的调用执行更改为:

data test3;
    set test2;
    call execute('%dummy('||strip(ds)||','||%str(strip(statement))||')');
run;

但是没有用。有人有什么想法吗?

感谢您的帮助!!

传递等号很容易,只需在调用中使用命名参数即可。

%dummy(ds=x,statement=x=2)

传递分号真的很难。为此,您需要在宏调用中引用值。您可以使用宏引用来完成,但我发现使用普通引号并在宏代码中将其删除更容易。

使用 DEQUOTE() 函数让宏删除任何可能围绕参数值的引号。

%macro dummy(ds,statements);
data &ds.2;
  set &ds.;    
  %sysfunc(dequote(&statements))
run;
%mend;

让我们将您的示例语句设置为数据集。

data have;
  ds='x';
  statements=
   'j1=input(j,anydtdtm.); drop j; rename j1=j;'
|| 'k1=input(k,anydtdtm.); drop k; rename k1=k;'
|| 'l1=input(l,anydtdtm.); drop l; rename l1=l;'
  ;
run;

还有一些示例数据供这些语句操作。

data x;
 j='01JAN1960';
 k='10FEB2010';
 l='2014-05-01';
run;

现在您可以使用您的数据集生成调用。使用 QUOTE() 函数将语句括在引号中。

options mprint;
data _null_;
  set have ;
  call execute(cats('%nrstr(%dummy)(',ds,',',quote(trim(statements)),')'));
run;

这是日志。

1    + %dummy(x,"j1=input(j,anydtdtm.); drop j; rename j1=j;k1=input(k,anydtdtm.); drop k; rename
k1=k;l1=input(l,anydtdtm.); drop l; rename l1=l;")
MPRINT(DUMMY):   data x2;
MPRINT(DUMMY):   set x;
MPRINT(DUMMY):   j1=input(j,anydtdtm.);
MPRINT(DUMMY):   drop j;
MPRINT(DUMMY):   rename j1=j;
MPRINT(DUMMY):  k1=input(k,anydtdtm.);
MPRINT(DUMMY):   drop k;
MPRINT(DUMMY):   rename k1=k;
MPRINT(DUMMY):  l1=input(l,anydtdtm.);
MPRINT(DUMMY):   drop l;
MPRINT(DUMMY):   rename l1=l;
MPRINT(DUMMY):   run;

首先,如果您使用命名参数,这不是问题。

%macro dummy(ds=,statements=);
    data &ds.2;
        set &ds.;    
        &statements.
    run;
%mend;


data class;
  set sashelp.class;
run;

data fixes;
  infile datalines truncover;
  length statement  all_statements 2 execstr 24;
  do _n_ = 1 to 2;
      input @1 statement .;
      all_statements=catx(';',all_statements,statement);
  end;
  put all_statements;
  execstr = cats('%dummy(ds=class,statements=',all_statements,';)');
  call execute(execstr);
  datalines;
if sex='M' then m_height=height
if sex='F' then f_height=height
;;;;
run;

然后 SAS 看到命名参数等号并知道从那个到下一个逗号的所有内容都是该参数的值。

当然,如果有逗号,你还是要做点什么。这是您接近但不完全的地方。 %str 需要引用 宏调用 ,而不是宏调用的 构造 - 换句话说,它需要在内部引号。

data fixes;
  infile datalines truncover;
  length statement  all_statements 2 execstr 24;
  do _n_ = 1 to 2;
      input @1 statement .;
      all_statements=catx(';',all_statements,statement);
  end;
  put all_statements;
  execstr = cats('%dummy(ds=class,statements=%nrstr(',all_statements,';))');
  call execute(execstr);
  datalines;
if sex in ('M','F') then mfheight=height
if sex='F' then f_height=height
;;;;
run;

我个人喜欢这里的 %nrstr,因为它还消除了那些讨厌的符号和百分号,这可能有一些意义。

把它放在那里意味着当 SAS 运行调用 execute 时,它​​会传递 %str%nrstr 并引用正在发送的值。

当你在其他地方拥有它时:

execstr = cats('%dummy(ds=class,statements=',%nrstr(all_statements),';)');

protected/quoted 并不是 all_statements 中的文本,而是字符 all_statements(换句话说, 变量名 ).那对你来说真的没什么用。

阅读此处的评论后(感谢你们的帮助!),我想出了一个不同的解决方案(尽管更改我的宏以接受命名参数也有效):

data test3;
    set test2;
    statement=tranwrd(statement,"=",'%nrstr(=)');
    call execute('%dummy('||strip(ds)||','||strip(statement)||')');
run;

基本上,我只是将 "code" 改成了这样:

j1%nrstr(=)input(j,anydtdtm.); drop j; rename j1%nrstr(=)j; k1%nrstr(=)input(k,anydtdtm.); drop k; rename k1%nrstr(=)k; l1%nrstr(=)input(l,anydtdtm.); drop l; rename l1%nrstr(=)l;

我注意到如果我将宏更改为

%macro dummy(ds=,statements=);
data &ds.2;
  set &ds.;    
  &statements.
run;
%mend;

那么此解决方案无效。它说 "more positional parameters found than defined"。任何想法为什么?

所以宁愿将语句传递给宏,因为我假设你也生成要传递给宏的语句,我只会传递变量名,原始的和期望的,并使用数组。

%macro converter(var_in= , var_out=);
    data want;
      set data;
      array have(*) &var_in;
      array want(*) &var_out;

      do i=1 to dim(have);
         want(i)=input(have(i), anydtdtm.);
      end;
      drop &var_in;
     run;
 %mend;


 %converter(var_in= j1 k1 l1, var_out= j k l);

编辑:基于comments.To 的版本 2 简化了过程,我将变量重命名为 temp1-temp{numvars},然后将它们重新编码为所需的变量。这是测试成功的。

%macro converter(var_in= j k l);
%let n_vars = %sysfunc(countw(&var_in));
    data want;
      set data (rename = (%do i=1 %to &n_vars;
                            %scan(&var_in, &i)=temp&i
                            %end;));
      array have(*) temp1-temp&n_vars.;
      array want(*) &var_in;

      do i=1 to dim(have);
         want(i)=input(have(i), anydtdtm.);
      end;
      drop temp1-temp&n_vars;
     run;
 %mend;

 %converter(var_in= j k l);

这是在文件中生成代码然后使用 %include 语句提交它可能更容易的情况之一:

filename tempsas temp;
data test3;
  set test2;
  file tempsas;
  put
    'data ' ds +(-1) '2;' /
    '  set ' ds ';' /    
    '  ' statements /
    'run;'
    ; 
run;

然后您可以从查看文件开始(在编辑 window 中使用 'include tempsas' 命令),提交一个数据步骤以查看是否一切顺利,当您确定一切都很好,你把

%include tempsas;

在您的原始代码中。