3

我是来自 python、java 和 C++ 的 SAS 新手。在这些语言中,编写/重复大型语句时正确的做法是将它们封装在一个变量中,该变量在代码中定义一次并重复多次。

即,每次合并两个相似的数据集时,我不想写相同的 where 语句,而是写:

WHERE_CONDITION_VARIABLE = 'X in (10, 100, 1000, 10000 ......100000000);

data output;
merge in1 in2;
WHERE WHERE_CONDITION_VARIABLE;
run;

data output2;
merge in3 in4;
WHERE WHERE_CONDITION_VARIABLE;
run;

不幸的是,我还没有弄清楚如何定义一个变量,比如 WHERE_CONDITION_VARIABLE 来简化代码。我要问的可以在 SAS 中做吗?

4

3 回答 3

4

您可以使用宏变量。您可以这样定义它们:

%let WHERE_CONDITION_VARIABLE = X in (10, 100, 1000);

并像这样引用它们:

&WHERE_CONDITION_VARIABLE
于 2013-03-07T09:12:54.880 回答
1

SAS 有很多选项可以避免重复代码;这样,它实际上很像 python,尽管完成它的方法有点不同,因为你有一个单独的编译步骤(所以你不能像你直接问的那样直接说 WHERE)。

首先,你有宏变量。如果您只是多次重复文本,您可以在宏变量中定义它,如下所示:

%let condition=X in (1,10,100,1000);

宏变量被视为您编写的文本。它们不需要引号或其他文本限定符,除非它们旨在将它们包含为法律代码,即:

%let condition=X in ('A','B','C');

是合法的,但是

%let condition="X in ('A','B','C')";

可能不是您想要的(除非您希望将其评估为字符串,无论如何)。

通过宏变量,您还可以在数据步中生成大量代码,然后将其包含在内。例如,如果您有一个包含条件列表的数据集,您可以通过以下方式应用它们:

data conditions;
format condition $50.;
input condition $;
datalines4; 
if x = 15 then y=5;
if x = 20 then y=10;
if x = 20 and z = 5 then y=15;
if x = 20 and z = 10 then y=20;
;;;;
run;

proc sql;
 select condition into :condlist separated by ' ' from conditions;
quit;

data want;
set have;
&condlist;
run;

这将从“条件”数据集中获取条件并将其推送到宏变量“&condlist”中。PROC SQL 调用是将其放入宏变量的最简单方法,但还有其他方法;CALL SYMPUT 也可以在数据步骤中执行此操作,或者您可以将其写入文本文件,然后 %include 文本文件作为代码。这更常用于高级编程,通过生成对宏的调用,条件数据集提供宏参数;在这种情况下,您可能有一个宏

%macro cond(x=,y=,z=);
 if x=&x and z=&z then y=&y;
%mend cond;

然后,您可以从只有 x,y,z 值的数据集生成对 cond 的调用:

proc sql;
 select cats('%cond(x=',x,',y=',y,',z=',z,')') into :condlist separated by ' ' from conditions;
quit;

并以同样的方式使用它。

一般来说,宏编程是避免代码蠕变的好方法;宏被编写一次,然后可以使用不同的参数运行多次。宏可以是任何地方,从在数据步骤中执行的一行代码(如上)到包含多个 DATA 和 PROC 步骤的数百行代码。宏编程本身就是一个复杂的话题,值得一读。


您还可以在 SAS 中编写函数。PROC FCMP(函数编译)允许您编写相当复杂的函数并在您的数据步骤甚至您的 PROC 语句中执行它们。 如果您有 9.2, http://www.lexjansen.com/pharmasug/2011/tu/pharmasug-2011-tu07.pdf是开始 FCMP 的好地方;如果你有 9.3,我还没有看到任何文件(但可能有一些)显示 FCMP 中的新内容。FCMP 是相当新的,因此在 SAS 的每次迭代中仍有很多变化。

这是一个 FCMP 的例子来做你的条件:

proc fcmp 
 outlib=work.funcs.Test; /* where will the functions be saved */ 
 function condition(x); /* declare a function returning a number */ 
 if x in (1,10,100,1000) then return(1);
 else return(0);
endsub; 
quit; 


data have;
do x = 1,5,10,20,100,150,1000,1500;
output;
end;
run;

options cmplib=work.funcs; 
data want;
set have;
if condition(x) then output;
run;

您还有 CALL EXECUTE 语句,它允许您直接从数据集中执行代码。使用相同的 CONDITIONS 数据集:

data _null_;
 set conditions end=eof;
 if _n_ = 1 then call execute('data want; set have;');
 call execute(condition);
 if eof then call execute('run;');
run;

这将有效地构建一个数据步骤,该步骤在您的数据空步骤之后立即执行,其代码与宏变量示例中的代码相同。调用执行的工作方式略有不同,因此虽然在此示例中应该没有任何区别,但有一些时间问题可能会导致问题(或可能是有利的);您使用哪个取决于具体情况。特别是对于 CALL EXECUTE,请阅读文档和在线论文(最常见的是 SUGI 论文)以了解更多详细信息。


除了通过宏变量或 CALL EXECUTE 直接执行代码之外,您还有很多其他执行任务的方法来避免墙纸代码。例如,为了更轻松地执行上述 if 语句,您可以使用一种格式。格式将一个值转换为另一个值;最常见的是,您可能有类似“DOLLAR6.2”之类的东西,它可以从数字 3.5 中给您 3.50 美元。但是,格式也可用于替换 if-this-then-that 表达式。如果只有 X 和 Y(没有 Z 条件),那么你可以这样做,给定这个条件数据集:

data conditions;
input x y;
datalines;
1 5
2 10
3 20
4 50
5 100
;;;;
run;

data for_fmt;
set conditions;
rename x=start y=label;
fmtname='XTOY';
type='i'; *type=i means numeric informat, so numeric to numeric conversion.  Informat = to numeric, Format= to character.;
run;

proc format cntlin=for_fmt;
quit;

data want;
set have;
y = input(x,XTOY.);
run;

有一行代码将 x 转换为 y。(当然有一些代码设置格式,但它可以与主代码分开,并包含在代码的设置部分中,如 c 中的 .h 文件)。

您还可以进行哈希表查找,这在您进行更复杂的转换(1 对多或多对 1)时非常有用。它们的工作方式就像听起来一样 - 您将哈希表加载到内存中并执行查找。 http://support.sas.com/rnd/base/datastep/dot/hash-getting-started.pdf是一个很好的起点。


最后,避免重复代码的一种好方法是使用更少的单独数据集。SAS 数据步骤和过程具有可用的“BY”语句,这意味着它们将 BY 变量的每个不同值视为有效的单独数据集。变量名称和长度需要匹配,因为它在技术上仍然是一个数据集,但是如果您有许多相似数据的数据集,并且希望对每个数据集执行相同的操作,您可以使用 BY 语句执行一次而不是多次.

例如,假设您有数据集 SASHELP.CARS。您可能想为每种汽车品牌单独计算一些东西。你可以这样做:

data acura;
set sashelp.cars;
if make='ACURA';
run;    

data honda;
set sashelp.cars;
if make='HONDA';
run;

然后分别在每个数据集上运行您的代码。然而,一个更 SASsy 的方法是使用 BY 语句:

proc means data=sashelp.cars;
by make;
var mpg_city mpg_highway;
run;

现在,每个品牌都有一个单独的页面。您也可以在数据步处理中使用 BY 语句;你会得到变量 FIRST.make 和 LAST.make ,它们会告诉你你是在新 MAKE 的第一条记录上还是在 MAKE 的最后一条记录上(值变化之前的记录),这使你可以根据关于您在数据集的 BY 组中的位置(例如,if first.make then counter=0;将允许您拥有一个计数器,每次您在make.在使用它之前(或在该变量上有一个索引,或两者兼而有之)。这对于分析 bootstrap 样本或其他拥有许多几乎相同数据集并对它们执行相同操作的过程非常有帮助。

于 2013-03-07T16:15:08.543 回答
-2

我假设您想将所有 WHERE 条件变量放入一个桶中,然后根据类似结构的索引 (Python) 使用它们。

如果是这种情况,那么您可能想看看“INTO”。在“INTO”中,您将放弃所有的 X。然后你可以随时拿走它们。

于 2013-03-07T06:14:00.727 回答