【问题标题】:Dynamically call macro from sas data step从 sas 数据步骤动态调用宏
【发布时间】:2020-09-22 23:33:23
【问题描述】:

当作为 SAS 程序运行时,此代码执行良好:

%MyMacro(foo_val, bar_val, bat_val);

我创建了一个表:

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

我想为analyses 表的每一行执行一次MyMacro

以下代码似乎只将字符串值titleweightresponse(而不是数据值foo1 等)传递给我的宏(通过调用%put 命令进行了测试) :

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

RUN;

如何在将数据值作为参数传递给宏的同时,为analyses 表的每条记录调用一次宏?其目的是实际运行此程序以进行大量分析,因此解决方案必须适当地扩展到 analyses 表中的更多记录。

【问题讨论】:

    标签: sas sas-macro


    【解决方案1】:

    这部分取决于您的宏正在做什么。如果我们假设您的宏正在执行旨在在数据步骤之外运行的操作(即,它不仅仅是分配数据步骤变量),那么您有多种选择。

    CALL EXECUTE 已经解释过了,在某些情况下是一个不错的选择。然而,它有一些缺点,特别是在宏定时方面,在某些情况下需要格外小心来保护 - 特别是当您在宏中创建宏变量时。 Quentin 在他的 cmets 中展示了一种解决此问题的方法(将 %NRSTR 添加到调用中),但我发现我更喜欢仅在使用 CALL EXECUTE 比其他方法有优势时才使用 - 特别是,如果我想使用 SAS 数据步骤技术(例如 FIRST 或 LAST,或某种形式的循环)来创建我的宏调用,或者当我必须在数据步骤中做一些事情并且可以避免再次读取文件的开销时.如果我只是在写一个像你上面那样的数据步骤——数据、设置、调用执行、运行——我不会使用它。


    PROC SQL SELECT INTO 通常是我用于列表处理的东西(这在很大程度上就是这样)。在做不太复杂的事情时,我更喜欢 SQL 的简单性。例如,您可以使用DISTINCT 轻松获得每个宏调用的一个版本,而无需显式编写proc sort nodupkey 或使用第一个/最后一个处理。它还具有调试的优势,您可以将所有宏调用写入结果窗口(如果您不添加noprint),如果我尝试,这比我的日志更容易阅读看看为什么我的调用没有正确生成(并且不需要任何额外的 PUT 语句)。

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

    它们的运行非常简单,并且没有时间问题(因为您只是在编写一堆宏调用)。

    这种方法的主要缺点是宏变量中最多有 64k 个字符,因此如果您要编写大量这些字符,就会遇到这种情况。在这种情况下,请使用 CALL EXECUTE%INCLUDE 文件。


    %INCLUDE 文件在调用超过字符限制时作为SELECT INTO 的替换非常有用,或者如果您发现有一个文本文件来查看您的调用很有用(如果您正在运行此例如,在批处理模式下,这可能比日志或列表输出更容易获取和/或解析)。您只需将调用写入文件,然后 %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;
    

    我真的不再使用这种技术了,但它是一种常见的技术,特别是老 SAS 程序员使用的技术,非常值得了解。


    DOSUBL 是一种相对较新的方法,在某种程度上可以用来替代CALL EXECUTE,因为它的默认行为通常比CALL EXECUTE 更接近您直观的预期。文档页面确实是如何以不同方式工作的最佳示例;基本上,它通过让每个单独的调用看起来从调用环境导入和导出宏变量来解决时间问题,这意味着DOSUBL 的每次迭代都在不同的时间运行,而CALL EXECUTE 的所有内容都在一个运行一堆并且宏环境是“固定的”(即,对宏变量的任何引用在运行时都是固定的,除非你用%NRSTR 将其转义)。


    还有一点值得一提的是RUN_MACRO,它是FCMP 语言的一部分。这允许您完全运行宏并将其内容导入回数据步骤,这在某些情况下是一个有趣的选项(例如,您可以围绕选择计数的 PROC SQL 包装调用的东西,然后将其作为变量导入数据集,所有这些都在一个数据步中)。如果您这样做是为了调用宏来分配数据步骤变量,而不是运行一个执行不需要导入数据步骤的事情的进程,那么它是适用的,但如果您这样做是值得考虑的确实希望将这些数据全部返回到调用该过程的数据集中。

    【讨论】:

    • 非常好的总结。我仍然希望更多文档来自 SAS re DOSUBL 和 RUN_MACRO。在我看来,这两者都以某种方式在单独的迷你 SAS 会话中执行代码。几年前我使用它时,范围规则不是很清楚。例如,在使用 run_macro 调用的宏中设置的系统选项可能会也可能不会更改主会话的选项。但它们无疑是强大的工具,值得学习。
    • 不错的答案。还值得考虑的是 - 可以将宏重写为 FCMP 函数吗?
    • 我期待 FCMP 函数的数量超过宏的那一天......虽然在这种情况下,这听起来像是一个完整的分析,现在最好不要这样写(而 RUN_MACRO允许它以这种方式编写,如果没有其他主要好处,我不相信这真的是一个好主意)。
    【解决方案2】:

    您可以使用 CALL EXECUTE:

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

    【讨论】:

    • 我喜欢调用执行方法。请注意,在执行数据 null 步骤时,将执行宏(解析所有宏符号)。这意味着如果 %MyMacro 从数据步骤代码生成宏变量,它将不起作用。通常添加 %nrstr() 会很有帮助,它会让 call execute 生成宏调用,但宏直到 data null 步骤完成后才会执行,例如:call execute(' %nrstr(%MyMacro('||title||','||weight||','||response||'))');见support.sas.com/kb/23/134.html
    • 另见 DOSUBL 作为 CALL EXECUTE 的替代品,在同一支持页面中引用,显然可以管理时间问题,而无需在 %NRSTR() 中包装宏调用
    【解决方案3】:

    您可以将变量值放入宏变量中,然后以宏变量作为参数多次调用%MyMacro(数据集中的obs数):

    数据:

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

    运行宏的代码:

    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;
    

    【讨论】:

    • 看起来很老套,我不明白为什么我不能从 DATA 步骤中调用宏 - 你能解释为什么这种方法是必要的吗?
    • 将第二个 %DO 更改为 %TO
    • 您的方法因时间问题而失败。在执行数据步骤代码之前执行宏语句。这是因为宏语言的工作是生成 SAS 代码。在您的示例中,宏将标题视为文本字符串参数。它不知道有一个名为 title 的数据集变量有一个值。调用执行方法更符合您的预期。它是一个数据步骤语句,在执行时可以调用宏,并且可以从数据集值传递参数。那里也有有趣的时间问题。 (请参阅我的评论)。
    • @Quentin 在时间上是正确的。宏为您编写代码。宏处理器执行,然后将生成的字符串传递给 SAS 编译器进行编译和运行。
    • 是的,这是我的错,我删除了一条附加评论,因为我收到了一个错误(这是因为我只在两个地方之一将 varname 更改为其他内容,所以我没有t 执行 DATA 步的最后一行)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多