简体   繁体   English

从 sas 数据步骤动态调用宏

[英]Dynamically call macro from sas data step

This code executes fine when Run as a SAS program:当作为 SAS 程序运行时,此代码可以正常执行:

%MyMacro(foo_val, bar_val, bat_val);

I have created a table using:我使用以下方法创建了一个表:

DATA analyses;
   input title : $32. weight : $32. response : $32.;
   datalines;
foo1 bar1 bat1
foo2 bar2 bat2
;

I want to execute MyMacro once for each row of the analyses table.我想对analyses表的每一行执行一次MyMacro

The following code appears to only pass the string values title , weight and response (rather than the data values foo1 etc.) to my macro (tested with calls to the %put command) :以下代码似乎只将字符串值titleweightresponse (而不是数据值foo1等)传递给我的宏(通过调用%put命令进行测试):

DATA _NULL_ ;
    set analyses;
    %MyMacro(title, weight, response);

RUN;

How can I invoke the macro once per record of the analyses table whilst passing data values as arguments to the macro?如何在将数据值作为参数传递给宏的同时,为analyses表的每个记录调用一次宏? The intention is to actually run this for a very large number of analyses so the solution must scale appropriately to many more records in the analyses table.目的是为大量分析实际运行它,因此解决方案必须适当地扩展到analyses表中的更多记录。

This in part depends on what your macro is doing.这部分取决于您的宏在做什么。 If we assume that your macro is doing something that is intended to be run outside of a data step (ie, it's not just assigning a data step variable), then you have several options.如果我们假设您的宏正在执行一些旨在在数据步骤之外运行的操作(即,它不仅仅是分配数据步骤变量),那么您有多种选择。

CALL EXECUTE has already been explained, and is a good option for some cases. CALL EXECUTE 已经解释过了,在某些情况下是一个不错的选择。 It has some downsides, however, particularly with macro timing, that requires some extra care to protect in some cases - particularly when you are creating macro variables inside your macro.然而,它有一些缺点,特别是在宏时序方面,在某些情况下需要额外小心保护 - 特别是当您在宏中创建宏变量时。 Quentin in his comments shows a way to get around this (adding %NRSTR to the call), but I find that I prefer to only use CALL EXECUTE when there's an advantage to doing so over the other methods - particularly, if I want to use SAS data step techniques (such as FIRST or LAST, for example, or some form of looping) in creating my macro calls, or when I have to do things in a data step anyway and can avoid the overhead of reading the file another time. Quentin 在他的评论中展示了一种解决方法(在调用中添加%NRSTR ),但我发现我更喜欢只在 CALL EXECUTE 比其他方法有优势时才使用 CALL EXECUTE - 特别是,如果我想使用SAS 数据步骤技术(例如 FIRST 或 LAST,或某种形式的循环)在创建我的宏调用时,或者当我无论如何必须在数据步骤中执行操作时,可以避免再次读取文件的开销。 If I'm just writing a data step like yours above - data something, set something, call execute, run - I wouldn't use it.如果我只是写一个像你上面那样的数据步骤——数据一些东西,设置一些东西,调用执行,运行——我不会使用它。


PROC SQL SELECT INTO is typically what I use for list processing (which is largely what this is). PROC SQL SELECT INTO通常是我用于列表处理的(主要是这样)。 I like SQL's simplicity a bit better when doing things that aren't too complicated;在做不太复杂的事情时,我更喜欢 SQL 的简单性; for example, you can get just one version of each macro call easily with DISTINCT without having to explicitly write a proc sort nodupkey or use first/last processing.例如,您可以使用DISTINCT轻松获得每个宏调用的一个版本,而无需显式编写proc sort nodupkey或使用第一个/最后一个处理。 It also has the advantage for debugging that you can write all of your macro calls to your results window (if you don't add noprint ), which is a bit easier to read than the log for me if I'm trying to see why my calls didn't get generated properly (and doesn't take any extra PUT statements).它还具有调试优势,您可以将所有宏调用写入结果窗口(如果您不添加noprint ),如果我想了解原因,这比日志更容易阅读我的调用没有正确生成(并且没有使用任何额外的 PUT 语句)。

proc sql;
  select catx(',','%macro(',arg1,arg2,arg3)||')' 
    into :mvarlist separated by ' '
    from dataset;
quit;

&mvarlist.

That runs them quite simply, and has no timing issues (As you're just writing a bunch of macro calls out).这很简单地运行它们,并且没有时间问题(因为你只是在写一堆宏调用)。

The main downside to this method is that you have a maximum of 64k characters in a macro variable, so if you're writing a huge number of these you'll run into that.这种方法的主要缺点是宏变量中最多有 64k 个字符,因此如果您要编写大量字符,就会遇到这种情况。 In that case use CALL EXECUTE or %INCLUDE files.在这种情况下,请使用CALL EXECUTE%INCLUDE文件。


%INCLUDE files are largely useful either as replacement for SELECT INTO when the call is over the character limit, or if you find it useful to have a text file to look at with your calls (if you're running this in batch mode for example, this could be easier to get to and/or parse than log or listing output). %INCLUDE文件在调用超过字符限制时作为SELECT INTO替代品非常有用,或者如果你发现有一个文本文件来查看你的调用很有用(如果你在批处理模式下运行它例如,这可能比日志或列表输出更容易获取和/或解析)。 You just write your calls out to a file, and then %INCLUDE that file.您只需将调用写入一个文件,然后%INCLUDE该文件。

filename myfile temp; *or a real file if you want to look at it.;
data _null_;
 set dataset;
 file myfile;
 length str $200;
 str=catx(',','%macro(',arg1,arg2,arg3)||')';
 put str;
run;

%include myfile;

I don't really use this much anymore, but it's a common technique used particularly by older SAS programmers so good to know.我真的不再使用这么多了,但这是一种常用技术,特别是老 SAS 程序员使用的技术,很高兴知道。


DOSUBL is a relatively new method, and to some extent can be used to replace CALL EXECUTE as its default behavior is typically closer to what you expect intuitively than CALL EXECUTE 's. DOSUBL是一种相对较新的方法,在某种程度上可以用来代替CALL EXECUTE因为它的默认行为通常比CALL EXECUTE的更接近您直观的期望。 The doc page has really the best example for how this works differently;文档页面确实是说明其工作方式不同的最佳示例; basically, it fixes the timing issue by letting each separate call look import and export the macro variables from/to the calling environment, meaning that each iteration of DOSUBL is run at a distinct time versus CALL EXECUTE where everything is run in one bunch and the macro environment is 'fixed' (ie, any reference to a macro variable is fixed at run time, unless you escape it messily with %NRSTR ).基本上,它通过让每个单独的调用看起来从/向调用环境导入宏变量并将宏变量导出到调用环境来解决计时问题,这意味着DOSUBL每次迭代都在不同的时间运行,而不是在CALL EXECUTE中运行所有内容,并且宏环境是“固定的”(即,对宏变量的任何引用在运行时都是固定的,除非您使用%NRSTR其转义%NRSTR )。


One more thing worth mentioning is RUN_MACRO , a part of the FCMP language.还有一件事值得一提的是RUN_MACRO的一部分FCMP语言。 That allows you to completely run a macro and import its contents back to the data step , which is an interesting option in some cases (for example, you could wrap a call around a PROC SQL that selected a count of something, and then import that to the dataset as a variable, all in one datastep).这允许您完全运行宏并将其内容导入回数据步骤,这在某些情况下是一个有趣的选项(例如,您可以将调用包装在选择某个计数的 PROC SQL 周围,然后将其导入将数据集作为变量,全部在一个数据步骤中)。 It's applicable if you're doing this for the purpose of calling a macro to assign a data step variable, not to run a process that does things that don't need to be imported into the data step, but it's something worth considering if you do want that data back all in the dataset that called the process.如果您这样做是为了调用宏来分配数据步骤变量,而不是运行一个执行不需要导入数据步骤的事情的进程,那么它是适用的,但是如果您这样做,这是值得考虑的事情确实希望该数据全部返回到调用该过程的数据集中。

You could use CALL EXECUTE:您可以使用 CALL EXECUTE:

data _null_;
  set analyses;
  call execute('%nrstr(%MyMacro('||title||','||weight||','||response||'))');
run;

You can put the variables values into macrovariables and then call your %MyMacro many times (the number of obs in your dataset) with the macrovariables as argument:您可以将变量值放入宏变量中,然后使用宏变量作为参数多次调用%MyMacro (数据集中的 obs 数量):

Data :数据 :

DATA analyses;
   input title : $32. weight : $32. response : $32.;
   datalines;
foo1 bar1 bat1
foo2 bar2 bat2
;
run;

Code to run macro :运行宏的代码:

data _NULL_;
    set analyses end=fine;
    call symput("ARGUMENT"||compress(_N_),catx(",",title,weight,response));
    if fine then call symput("NLOOPS",compress(_N_));
run;
%*PUT &ARGUMENT1;
%*PUT &ARGUMENT2;

%MACRO MAIN;
%DO L=1 %TO &NLOOPS;
    %MyMacro(&&ARGUMENT&L);
%END;
%MEND;
%MAIN;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM