在宏程序中调用execute不会改变数据文件

Call execute in macro programme won't change the data file

我编写了这个宏,它使用调用 execute 将我的旧变量的标签放在我的新变量上。但是,当我在不同的数据集上使用宏时,调用执行不会更改数据集。

%MACRO CHARTONUM2(file=,var=,nbvar=,newvar=,fmt=);
%DO aa=1 %TO &nbvar;
    DATA &file;
        set &file end=eof;
        &newvar&aa=input(put(&var&aa,.),3.);
        format &newvar&aa &fmt..;
        if eof then do;
            call execute('data &syslast; set &syslast;');
            call execute('label &newvar&aa = "' || strip(vlabel(&var&aa)) || '";');
            call execute('run;');
        end;
        drop &var&aa;
    RUN;
%END;
%MEND CHARTONUM2;

因此,数据集是带有参数 "file" 的动态数据集。如果我 运行 这个宏两次使用 2 个不同的数据集,调用 execute 将在第一个数据集中搜索我的变量。剩下的 运行 宁好。这是日志:

%CHARCHARTONUM(file=demohealth,var=SEX,deb=1,end=1,newvar=sexn,fmt=
sex);
MPRINT(CHARCHARTONUM):   DATA demohealth;
MPRINT(CHARCHARTONUM):   set demohealth end=eof;
MPRINT(CHARCHARTONUM):   sexn=input(put(SEX, $sex.),3.);
MPRINT(CHARCHARTONUM):   format sexn sex.;
MPRINT(CHARCHARTONUM):   if eof then do;
MPRINT(CHARCHARTONUM):   call execute('data &syslast; set &syslast;');
MPRINT(CHARCHARTONUM):   call execute('label &newvar = "' ||
strip(vlabel(SEX)) || '";');
MPRINT(CHARCHARTONUM):   call execute('run;');
MPRINT(CHARCHARTONUM):   end;
MPRINT(CHARCHARTONUM):   drop SEX_STD;
MPRINT(CHARCHARTONUM):   RUN;

WARNING: The variable SEX_STD in the DROP, KEEP, or RENAME list has never
         been referenced.
MPRINT(CHARCHARTONUM):   data WORK.ASIPRE ;
MPRINT(CHARCHARTONUM):   set WORK.ASIPRE ;
MPRINT(CHARCHARTONUM):   label sexn = "Sex";
MPRINT(CHARCHARTONUM):   run;

我想在任何数据集上启动这个宏...有人有想法吗?

它与 &SYSLAST 的值何时更新以及该数据步骤的编译何时发生有关。

CALL EXECUTE 数据步骤的编译发生在内部数据步骤完成之前。 &SYSLAST 的值尚未更新。您的版本有效是因为 &FILE 的值与 &SYSLAST

相同

你可以通过这个例子看到它:

data test;
x=1;
label x="X Label";
run;

data temp;
y=2;
label y="Y Label";
run;

%macro NUMTOCHAR(file=,var=,newvar=,fmt=best.);
data &file;
set &file end=eof;
&newvar=put(&var,&fmt);

if eof then do;
    call execute('data &syslast; set &syslast;');
    call execute('label &newvar = "' || strip(vlabel(&var)) || '";');
    call execute('run;');
end;
run;

%mend;

%numtochar(file=test,var=x,newvar=xc);

您看到第二个数据步骤正在 temp 而不是 test

将您的代码更改为:

call execute('data &file; set &file;'); 

这是一个时间问题。当 call execute() 生成的代码被压入堆栈时,计算 &syslast 的值。您可以使用 %nrstr() 延迟评估,直到代码从堆栈中拉出以执行。

call execute('%nrstr(data &syslast; set &syslast;)');

运行这个例子来查看它的实际效果。

data one; x=1; run;
data two; x=2;
  call execute('data _null_; set &syslast; put x=; run;');
  call execute('data _null_; set %nrstr(&syslast); put x=; run;');
run;

日志

NOTE: CALL EXECUTE generated line.
1   + data _null_; set WORK.ONE                             ; put x=; run;

x=1
NOTE: There were 1 observations read from the data set WORK.ONE.


2   + data _null_; set &syslast; put x=; run;

x=2
NOTE: There were 1 observations read from the data set WORK.TWO.

但是因为你已经是运行一个宏了,真正的解决办法是修改你的宏,这样你就不需要使用call execute了。您还可以在一次数据传递中创建新变量。并消除不需要的 PUT() 函数调用。并使格式附加到新变量更加灵活甚至可选。

%MACRO CHARTONUM2(file=,var=,nbvar=,newvar=,fmt=);
%if %length(&fmt) and not %index(&fmt,.) %then %let fmt=&fmt..;
data _null_;
  set &file ;
%DO aa=1 %TO &nbvar;
  call symputx("label&aa",vlabel(&var&aa),'L');
%END;
  stop;
run;

date &file;
  set &file end=eof;
%DO aa=1 %TO &nbvar;
  &newvar&aa=input(&var&aa,32.);
  format &newvar&aa &fmt;
  label &newvar&aa="&&label&aa" ;
  drop &var&aa;
%END;
run;
%MEND CHARTONUM2;

不要为了更改标签而重写整个数据集——这是不必要的 I/O,如果数据集很大,那么代价可能会非常高。一次性执行所有转换,同时将动态标签语句生成为宏变量,以供后续 PROC DATASETS 永久分配新标签。

重复调用 numToChar 一次做一个变量也太过分了 I/O。

当您想要将多个变量转换为格式呈现表示中的副本时,请考虑将 space 分隔列表作为您的宏参数传递;变量名称列表、新名称列表和渲染格式列表。但是,在许多情况下,您可能会发现,命名约定和处理通用角色的变量可能只需要应用变量列表和通用格式。

最后,将一个变量存储在一个新变量中的需要,因为它的格式呈现表示可能是无关紧要的。对变量及其下游利用的审查可能表明您只需要将格式应用于原始变量。

示例代码

data have;
  x=12; y=12.34; z=0.007;
  label 
    x = 'The value of X'
    y = 'Y?, ask Mickey'
    z = 'Zed''s "bike"'
  ;
run;

%macro numToChar(data=, out=&data, vars=, newvars=, formats=);
  %local var newvar fmt labels lib mem;

  data &out;
    set &data;
    %do i = 1 %to %sysfunc(countw(&vars,,S));
      %let var    = %scan(&vars,&i,,S);
      %let newvar = %scan(&newvars,&i,,S);
      %let fmt    = %scan(&formats,&i,,S);

      &newvar = put(&var,&fmt);

      call symput('labels', catx(' ', symget('labels'), "&newvar=" || quote(trim(vlabel(&var)))));
    %end;
  run;

  %let lib = %scan(&syslast,1,.);
  %let mem = %scan(&syslast,2,.);

  proc datasets nolist lib=&lib;
    modify &mem;
    label &labels;
  run;
  quit;
%mend;

options mprint;

%numToChar(
  data=have,
  out=want,
  vars=x y z,
  newvars=x_best y_best z_best,
  formats=best. best. best.
);