在sas中定义变量来清理代码

Posted

技术标签:

【中文标题】在sas中定义变量来清理代码【英文标题】:Defining variables in sas to clean up code 【发布时间】:2013-02-22 05:16:51 【问题描述】:

我是来自 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 中做吗?

【问题讨论】:

对您的代码的评论 - 我假设您没有在没有 BY 语句的情况下运行此代码,对吗? SAS 合并不像 SQL 逗号连接。您不能基于 WHERE 语句进行合并。 【参考方案1】:

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

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

并像这样引用它们:

&WHERE_CONDITION_VARIABLE

【讨论】:

【参考方案2】:

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;

这将有效地构造一个数据步骤,该步骤在您的数据 null 步骤之后立即执行,其代码与宏变量示例中的代码相同。调用执行的工作方式略有不同,因此在此示例中应该没有任何区别,但有一些时间问题可能会导致问题(或可能是有利的);您使用哪个取决于具体情况。特别是对于 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 中有新值时重置一个计数器。)对于 BY 组的唯一警告是您必须进行排序您的数据集在使用它之前由 BY 变量(或在该变量上有一个索引,或两者兼而有之)。这对于分析 bootstrap 样本或您拥有许多几乎相同的数据集并对它们执行相同操作的其他过程非常有帮助。

【讨论】:

感谢您提供非常完整的答案!【参考方案3】:

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

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

【讨论】:

以上是关于在sas中定义变量来清理代码的主要内容,如果未能解决你的问题,请参考以下文章

在 SAS 中,如果空变量不存在,是不是有更快的方法来创建它?

创建 SAS 宏来处理任意数量的变量

sas怎么定义一个宏变量求n项和

SAS代码错误地将宏变量的值作为输出中的新列/变量传递

SAS:使用用户定义格式时,如果不匹配,“默认值”是未格式化的输入变量吗?

通过在 SAS 中用另一个前缀替换前缀来动态重命名变量