【问题标题】:Executing SAS Code from string in dataset从数据集中的字符串执行 SAS 代码
【发布时间】:2013-07-30 09:49:42
【问题描述】:

是否可以读取(并执行)存储在 SAS 数据集中的字符串中的 SAS 代码。

例如,数据集“CODE”包含一个字符串变量,其中包含

"IF TOTAL_SALES GE 20000 AND TYPE IN ('A', 'B', 'C') THEN VAR1 = 'Y' ;"

我可以这样做吗?

data sales ;
set sales ;
/* run the if statement above */
run ;

使用 SAS 9.2

【问题讨论】:

  • 您能否提供有关该问题的更多详细信息?为什么代码存储在变量中?你是自己放的还是由外部来源提供的。如果您可以控制代码是如何从那里开始的,那么有很多更好的方法可以解决这些问题。
  • 我使用 SAS 代码生成代码。基本上,输入文件具有规则的详细信息(例如 CODE1EQA、SUMINSUREDGT20000、COVEREQC)等,我正在尝试将其转换为 SAS 代码(即 CODE1='A' AND SUMINSURED GT 20000 AND COVER='C')和运行这个。

标签: sas sas-macro


【解决方案1】:

执行此操作的方法与构造宏变量的方法一样多。最常用的三个:

proc sql select into

这使您可以立即创建多行代码,并且可能是用于此目的的最常用工具。它可以直接创建代码行,或者更有用地创建宏调用。例如,假设您想运行:

data want;
set have;
x = sum(a,b,c);
run;

但是 x,a,b,c 都是在另一个数据集中定义的。此外,您有 3 个这样的变量。

data callset;
input var1 $ var2 $ var3 $ var4 $;
datalines;
x a b c
y d e f
z b e c
;;;;
run;

你可以这样构造它:

proc sql;
select cats(x,"=sum(",a,",",b,",",c,");") into :calllist
 separated by ' '
 from callset;
quit;

data want;
set have;
&calllist.
run;

但是,构建宏可能更容易:

%macro sum(var1,var2,var3,var4);
&var1. = sum(&var2.,&var3.,&var4.);
%mend sum;

那么你的 PROC SQL 会更容易一些(在这种情况下并非如此,但这通常有助于更复杂代码的可读性):

proc sql;
select cats('%sum(',catx(',',x,a,b,c),')') into :calllist
 separated by ' '
 from callset;
quit;

然后以同样的方式使用它。

这里的限制:除了 PROC SQL 允许你做的事情之外,你不能在构造它时修改字符串(这是强大的,但不是 datastep 代码,如果你需要使用像 first.var 这样的东西,你必须这样做在单独的步骤中的 proc sql 之前)。宏变量中总共有大约 20k 个字符的限制。

顺便说一句,separated by 很重要;没有它,您只能创建一行代码(只有最后一行将被放入宏变量中)。即使你真的不希望它被任何东西分隔,你仍然需要用''分隔才能生成列表。

%包含文件

include 文件方法是proc sql 方法和调用execute 的混合体。它是在数据步骤中构建的,并且没有超出操作系统文件大小限制的长度限制。然而,它有点混乱(因为创建了一个临时文件)并且具有包含文件的正常限制,例如不包含数据行。

您以这种方式构建它(使用以前的数据集):

filename toincl temp; *create temporary fileref;
data _null_;
set callset;
file toincl;
callstr = cats('%sum(',catx(',',x,a,b,c),')');
put callstr $;
run;

data want;
set have;
%include toincl;
run;

它绕过了 PROC SQL 长度限制,但具有包含文件的正常限制(有关更多信息,请参阅the documentation)。

调用执行

这用于在数据步骤之后立即以交互方式执行一行代码。它很方便,因为它允许您比其他方法更灵活地动态构建代码,但它有很大的时间限制。

data _null_;
set callset; *this is not the main data set, but the control file with SAS code;
call execute('data want; set have;');
callstr=cats('%sum(',catx(',',x,a,b,c),')');
call execute(callstr);
call execute('run;');
run;

人们通常遇到的主要限制是宏变量时间。在 CALL EXECUTE 步骤中定义宏变量时,在同一 CALL EXECUTE 步骤中不能使用它。因此,如果您的代码包含要创建然后使用宏变量的代码,它将无法正常运行;您需要使用其他方法之一。如果您使用这种方法,我强烈建议您先阅读几篇关于 CALL EXECUTE 的论文,例如 this onethis one

【讨论】:

  • +1 用于提及使用 %include 的方法。干净、简单且可追溯。
【解决方案2】:

两种方法:

  1. 在数据步骤中使用 call symput 将其放入宏变量中。然后,您将宏变量放在您希望代码执行的位置。 (考虑 SAS 何时解释和执行什么的警告)
  2. 在数据步骤中使用调用执行。请注意,输入到调用执行中的所有代码都在数据步骤期间累积,然后在数据步骤之后立即解释和执行

调用执行见:http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a000543697.htm

【讨论】:

  • 我不推荐CALL SYMPUT;这不太可能比其他潜在的解决方案更好(或几乎一样好),而且要适合新用户非常复杂。
  • @Joe 我同意这对新用户来说很棘手。但它提供了优于调用执行的优势(无需创建完整的 proc/data 步骤 + 不一定必须在 datap 步骤之后立即使用)。就个人而言(当然口味不同),我发现它比带有 select into 的 proc sql 更方便,因为您可以在使用 call symput 之前在多个语句中更轻松地构建字符串。如果您不介意在数据步骤中创建完整的 proc/data 步骤,那么包含的优点是可以轻松(和永久)访问生成的代码。所以我同意这主要包括“胜利”。
  • 您真正希望多久执行一次一行行代码(或宏调用或其他)?大多数时候,您要么运行一堆调用,每行一个,在这种情况下,选择或包含文件比长连接的保留字符串更好,或者如果你只需要一个,通常更容易调用它proc sql 或调用执行。我什至不会向新手用户甚至中级用户提及 call symput,因为出现问题/混淆的可能性比实用程序要糟糕得多。
【解决方案3】:

在读入sales 数据之前,您希望在创建包含 SAS 代码的宏变量之前使用 call symput 函数。下面是一些示例代码:

* _null_ means there is no output dataset;
data _null_;
    set code;

    * assuming char variable containing the SAS code is called 'code_string';
    * if only certain rows contain the correct code_string values;
    * then you could use an if statement to assign &my_code as desired;
    call symput('my_code', code_string);
run;

data sales;
    set sales;

    * now access the value of the macro variable created with call symputx above;
    &my_code
run;

如果code具有包含SAS代码字符串的char变量的数据集只有一行(或者可以用where语句限制为一行),那么可以使用proc sql来实现与上述_null_ 数据步骤相同的结果:

proc sql;
    select code_string into :my_code
    from code
    /* where [put your boolean expression here] */
;
quit;

【讨论】:

  • 同上,不推荐CALL SYMPUT;这是将代码转换为宏变量的一种非常混乱的方法(就像您的 SQL 解决方案一样仅依赖于一行)。
  • 如果您阅读我的回答,您会看到我提到根据代码数据集的性质(他没有详细说明),他可以很容易地使用“if”或“where”语句仅在满足适当条件时分配宏变量。他的问题没有任何暗示他需要组合来自多行的值来形成宏变量。您如何抨击我的解决方案然后在您的回答中使用proc sql select into :macro_var 技术也有点讽刺。
  • 我只反对call symput,我发现对于新程序员来说很难理解并且几乎总是不如其他解决方案。您的proc sql 解决方案非常好,尽管您没有解释更常用的separated by 选项。你当然是对的,这个问题没有指定多行代码,但似乎更常见的用例是多行。
  • 另外,请不要将我的反对视为抨击,或以任何方式暗示这是一个错误的答案;当然,专家用户会找到使用call symput 的机会。我只是在SAS-L 上看到太多困惑的用户,认为这是对新用户的好建议。
  • 很公平。我想在有人对我投反对票后,我只是感到有点自卫。在我看来,唯一真正的障碍是理解宏语言。一旦掌握了这一点,proc sql select intocall symput 之间的差异对于单值分配就可以忽略不计了。对于不熟悉proc sql 的人来说,call symput 可能更容易上手,但您确实无法轻松过渡到引入select into 的连接能力。但我离题了。感谢您解释自己,干杯!
猜你喜欢
  • 1970-01-01
  • 2011-07-27
  • 2015-11-08
  • 1970-01-01
  • 2017-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多