编辑:我进一步阅读宏编程的主题,显然,如果宏被预编译,那么它的任何一个参数都不依赖于数据内容,因此这种做法并不好。还有其他选择吗?
在上一个问题之后,我遇到了一个问题。
我有一个宏,在其中,从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宏编程的一个关键方面(可能就是这种情况),我深表歉意,但我已经搜索过,发现没有其他解决方案可以解决我想做的事情。
提前感谢您的帮助。
答案 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;
注意我们将%do
与if
混合。我们正在使用%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;或其中一个边界条件。
基本上你应该如何在%IF
和IF
之间做出决定。虽然可以在宏中进行一些数据编程 - 但是你会看到很多人将数据移动到宏变量中,然后用它来驱动宏循环和类似的事情 - 通常会更难而不仅仅是在SAS工作。创建宏语言是为了能够执行上述操作;其他用途往往过于复杂,因为它并不是语言的意图。这样想吧 - 一个有用的机器人为你生成代码,所以你不必,但仍然生成代码,而不是实际上自己做的工作 - 并且你将避免很多混乱。