删除并有条件地编辑宏中缺少值的记录

时间:2014-04-09 11:52:05

标签: sas sas-macro

编辑:我进一步阅读宏编程的主题,显然,如果宏被预编译,那么它的任何一个参数都不依赖于数据内容,因此这种做法并不好。还有其他选择吗?

在上一个问题之后,我遇到了一个问题。

我有一个宏,在其中,从merged创建一个名为PROC SQL的数据集。此数据集看起来像这样(简化):

merged dataset
time  ret1  ret2
   1  0.01     . 
   3    .      . 
   4  0.04  0.04
   5  0.02  0.04

我的目标是删除缺少ret1和ret2的记录,并在所有记录中保留一个变量OBS = 1,其中ret1和ret2没有丢失。即

merged dataset
time  ret1  ret2 OBS     R12
   1  0.01     .   .       .
   4  0.04  0.04   1  0.0016
   5  0.02  0.04   1  0.0008

为此我创建了这个数据步骤。

data merged;
    set merged;
    OBS = 1;
    %if nmiss(of ret1 -- ret2) + cmiss(of ret1 -- ret2) > 1 %then
        delete;
    %else
        %if nmiss(of ret1 -- ret2) + cmiss(of ret1 -- ret2) > 0 %then
            %do;
                ret1 = . ;
                ret2 = . ;
                OBS = . ;
            %end;
    R12 = ret1 * ret2;
run;

如果我在宏之外使用它,它可以很好地工作,但在宏中它会给我以下错误,我不知道如何绕过它。

   ERROR: A character operand was found in the %EVAL function or %IF condition 
   where a numeric operand is required. The condition was: nmiss(of ret1 
   -- ret2) + cmiss(of ret1 -- ret2) > 1 

显然宏中的%IF语句不会评估nmiss或cmiss函数。我已经尝试了这个缺少的功能,但它也没有用。

所以我的问题是:是否可以在宏中执行我想要的操作?如果是这样,我的尝试是接近解决方案还是我需要从根本上改变它?如果这是一个简单的问题,或者我错过了SAS宏编程的一个关键方面(可能就是这种情况),我深表歉意,但我已经搜索过,发现没有其他解决方案可以解决我想做的事情。

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:4)

SAS中的宏编程存在的目的是以更有效的方式编写SAS语言语句,而不是手动编写它们。在大多数情况下,旨在与数据接口,甚至为您完成大部分工作;并且在一个宏观中与您是否使用%IF或IF无关。

从根本上说,宏是一种方法来处理你经常做的一堆复杂的事情,并用几次击键来做,而不是每次都写出来。键盘宏就是一个例子 - 设置" Ctrl + Shift + A"加载一个autoexec并运行它,比如说,比ctrl + o更有效,找到文件,打开,点击运行。它实际上并没有进行任何打开和运行 - 它只是取一个命令列表 - 打开,加载文件,运行文件 - 并一个接一个地执行它们。

SAS宏实际上是一回事。而不是输入

data want;
set sashelp.class;
if 11 LE age LE 12 then agerange = '11-12';
if 13 LE age LE 14 then agerange = '13-14';
if 15 LE age LE 16 then agerange = '15-16';
run;

你可以输入

%macro agerange(low,high);
if &low LE age LE &high then agerange = "&low.-&high.";
%mend agerange

然后

data want;
set sashelp.class;
%agerange(11,12);
%agerange(13,14);
%agerange(15,16);
run;

然后,最重要的是,我们添加了一个循环层,以便更容易地使用宏:

%macro agerange_bytwo(low,high);
%do L = &low %to %eval(&high-1) %by 2;
 %let H = %eval(&L+1);
 if &L LE age LE &H then agerange = "&L.-&H.";
%end;
%mend agerange_bytwo;

data want;
set sashelp.class;
%agerange_bytwo(11,16);
run;

注意我们将%doif混合。我们正在使用%do来控制我们想象的代码类型(决定数据步骤中的内容),并且我们正在使用if作为实际的SAS程序语句,这是我们的事情。想要真正输入我们的程序。

现在我们再添加一个步骤 - 我们添加一个低限和高限。

%macro agerange_bytwo(low,high);
%do L = &low %to %eval(&high-1) %by 2;
 %let H = %eval(&L+1);

 if 

 %if &L = &LOW %then 0; %else &L;
   LE age LE 
 %if &H = &HIGH %then 999; %else &H;

 then agerange = 

 %if &l = &low %then %do;
      "<= &H."
 %end;
 %else %if &h >= &high %then %do;
      "&L.+"
 %end;
 %else %do;
      "&L.-&H."
 %end;
 ;
%end;

%mend agerange_bytwo;

data want;
set sashelp.class;
%agerange_bytwo(11,16);
run;

这里我们将%if与if直接结合起来 - 而且最重要的是,我们实际上是在SAS语句的中间。请注意if ...then agerange =之后缺少分号。那是因为我们不想在那里放一个分号!我们还在各种%if陈述中将它们排除在年龄范围之外,主要是为了证明一点 - 如果我是真的写这个,我可能会把它们放在那里 - 但是,我把它移到后面%if - %end使其更加明显。另一方面,如果我们不使用%DO,我们确实需要一些分号来结束%then和%else部分。这是我几乎总是使用%DO的一个原因;它使分号对我更有意义。如果我只使用%if %else而不使用%do,我通常会第一次做错。

SAS声明

if 0 LE age LE 12 then agerange='<= 12';

所以我们从

开始
if

然后我们有代码决定是否应该放置&#34;&amp; L&#34;或&#34; 0&#34;那里;因为它决定了我们应该输入什么,所以在宏语言中,所以,%if。

所以:

if 0 LE age LE 

现在更多宏%如果决定放H。

离开我们

if 0 le age le 12 then agerange = 

最后一组%if条件决定是否输入&#34;&amp; L - &amp; H&#34;或其中一个边界条件。

基本上你应该如何在%IFIF之间做出决定。虽然可以在宏中进行一些数据编程 - 但是你会看到很多人将数据移动到宏变量中,然后用它来驱动宏循环和类似的事情 - 通常会更难而不仅仅是在SAS工作。创建宏语言是为了能够执行上述操作;其他用途往往过于复杂,因为它并不是语言的意图。这样想吧 - 一个有用的机器人为你生成代码,所以你不必,但仍然生成代码,而不是实际上自己做的工作 - 并且你将避免很多混乱。