【问题标题】:How exactly does call symput work - trying to create an iterator with help of call symputcall symput 究竟是如何工作的 - 尝试在 call symput 的帮助下创建一个迭代器
【发布时间】:2021-12-18 01:10:19
【问题描述】:

我正在编写修改上一个数据步骤中声明的数组的代码。由于它是一个新的数据步,旧索引将不起作用。我想我可以在 call symput 函数的帮助下使用迭代器。 我试图为每个 MID_(i) 数组元素分配 0 值,其中 month

data want;
set summary;
do i=1 to &MAX_MONTH.;
    call symputx('iterator',i);
    if MONTH < &iterator. then MID_&iterator. = 0;
end;run;

而且它不起作用。我正在试验代码来调试它并插入一个常量值而不是“i”:

data want;
set summary;
do i=1 to &MAX_MONTH.;
    call symputx('iterator',7);
    if MONTH < &iterator. then MID_&iterator. = 0;
end;run;

更让我困惑的是,这段代码只能运行一次。当我将“7”更改为其他数字时,结果保持不变,直到我重置 SAS,之后它将使用更改后的值,但仍然 - 只有一次。

这里发生了什么?我不明白什么?如何创建有效的迭代器?

【问题讨论】:

    标签: arrays loops sas iterator


    【解决方案1】:

    宏处理器首先将宏表达式转换为文本。所以&amp;MAX_MONTH&amp;iterator 在 SAS 开始编译数据步骤之前已经被它们的值替换了,而且肯定是在它有机会运行 CALL SYMPUTX() 或 IF 语句之前。

    因此,如果 MAX_MONTH 的值为 12,而 ITERATOR 的值为 7,那么您运行以下数据步骤:

    data want;
      set summary;
      do i=1 to 12;
        call symputx('iterator',i);
        if MONTH < 7 then MID_7 = 0;
      end;
    run;
    

    这和刚才跑的一样:

    data want;
      set summary;
      if MONTH < 7 then MID_7 = 0;
      i=13;
    run;
    %let iterator=12;
    

    ARRAY 语句是用于通过变量在列表中的位置来引用变量的数据步进方法。因此,如果您想引用名称为 MID_1、MID_2 等的变量,请定义一个数组并使用数组的索引。您仍然可以使用 MAX_MONTH 宏变量来定义要包含在数组中的变量集。

    所以也许你打算运行这样的东西:

    data want;
      set summary;
      array mid_ [&max_month] ;
      do index=month+1 to dim(mid_);
        MID_[index] = 0;
      end;
      drop index;
    run;
    

    【讨论】:

    • 您好,感谢您的全面回答。但是,我必须再问一个问题。如果我像您建议的那样声明一个新数组,它不会完全覆盖我之前存储的以前的值吗?我想对该数组执行进一步的计算,而不是完全擦除所有值。
    • 将变量添加到数组不会对变量进行任何更改。数组只是引用变量的工具。如果列出的变量(或在我的示例中隐含)不存在,则创建它们,但如果它们已经存在,则按原样使用。
    【解决方案2】:

    我建议完全使用数组,如果你的变量有命名约定,你就不需要其他任何东西了。

    我没有你的数据,但我想知道这样的简化是否也能奏效。

    data want;
        set summary;
        array mid_[*] mid_:;
    
        do i=1 to month-1;
             MID_[i] = 0;
        end;
    run;
    

    【讨论】:

      【解决方案3】:

      symputsymputx 在数据步骤结束后创建宏变量。正在创建的宏变量不能在同一数据步中访问。每次调用symput,都会更新最后输出的宏变量。

      根据call symputdocumentation

      您不能使用宏变量引用来检索 SYMPUT 创建的同一程序(或步骤)中的宏变量 该宏变量并为其赋值。

      您必须指定一个步骤 边界语句强制 DATA 步在之前执行 在程序后面的全局语句中引用一个值(对于 例如,一个 TITLE 语句)。边界可以是 RUN 语句或 另一个 DATA 或 PROC 语句。

      您不需要使用symput 来实现您的目标。 i 已经在迭代,如果您创建一个新的 mid_ 变量数组,就可以使用它。

      data want;
          set summary;
          array mid_[&MAX_MONTH.];
      
          do i=1 to dim(mid_);
              if MONTH < i then MID_[i] = 0;
          end;
      run;
      

      【讨论】:

        【解决方案4】:

        Stu、Tom 和 Reeza 都以你应该如何做的方式回答了这个问题。

        但是,为了完整起见,您可以使用宏变量执行此操作:也可以使用宏 %do。这不是解决您的确切问题的正确方法,但有些问题可能需要这种方法。

        %let max_month=12;
        
        data summary;
          do month = 1 to 12;
            output;
          end;
        run;
        
        %macro do_months(max_month);
        data want;
            set summary;
            %do i=1 %to &MAX_MONTH.;        
                if MONTH < &i. then MID_&i. = 0;
            %end;
        run;
        %mend do_months;
        
        %do_months(max_month=12);
        

        在这里,如果您打开 options mprint;,您可以看到 SAS 正在做什么:它为您创建了 12 个 if 语句,每个语句对于迭代器和 mid_ 变量都有不同的值。每次都通过数据步骤执行所有 12 个。它的效率不如数组解决方案,而且调试起来也困难得多,所以除非需要,否则不要这样做。

        【讨论】:

          【解决方案5】:

          Tom 就如何解决您的问题给出了很好的建议。然而,没有人解释 call symputx() 的工作原理:Stu Sztukowski 的回答部分不正确,因为它以错误的方式解释 SAS 文档。

          有两种语言:SAS Base(data step、proc sql 等)和 SAS Macro(%let、%put、&var 等)。所以有两个世界:SAS Base world 和 SAS Macro world。您可以使用特定功能在这两个世界之间交换数据。

          1.从 SAS Macro 访问 SAS Base 世界。 您可以使用特定的宏 %sysfunc() 执行大多数 SAS Base 函数。示例:

          %let mvSrc = there    are multiple  blanks;
          %let mvVar = %qsysfunc(compbl(%nrbquote(&mvSrc)));
          %put &=mvVar; /* prints MVVAR=there are multiple blanks */
          

          在宏变量前加上 mv 是一种很好的做法。 我还使用了宏 %nrbquote() 来处理带有特定于 SAS 宏符号(如括号、逗号、引号、& 符号等)的字符串。这里不是必需的,但它也是一个好习惯。

          2。从 SAS Base 代码访问 SAS Macro world。

          data _null_;
              call symputx('mvVar', 'hey!', 'g'); /* 1 */
              var = symget('mvVar');              /* 2 */
              put var=;                           /* 3 */
          
              call symputx('mvVar', 'whats' up?', 'g');
              var = symget('mvVar');
              put var=;
          run;
          

          输出如下:

          hey!
          what's up?
          

          它是如何工作的:

          1. 在全局范围内创建宏变量 mvVar,值为 hey!。
          2. 将最近的(本地,调用宏之一,...,全局)宏变量mvVar 读入数据步变量var
          3. 打印值。

          应该使用这些功能吗?至于调用 symputx() 答案是肯定的,你必须使用它来将数据放入宏观世界。至于 symget(),答案是有时你应该使用它,有时用简单的宏变量替换更容易,即var = "&amp;mvVar";。替换的问题是当宏变量包含双引号时它不起作用。比如下面的代码

          %let mvVar = hey, "Mr X"!;
          data _null_;
              var = "&mvVar";
          run;
          

          变成错误代码:

          data _null_;
              var = "hey, "Mr X"!"; /* error! */
          run;
          

          还要记住,替换只发生一次,在数据步骤被编译之前,因此在它被执行之前。以下代码

          %let mvVar = hello;
          data _null_;
              var = "&mvVar";
              put var=;
          
              call symputx('mvVar', 'yes?', 'g');
              var = "&mvVar";
              put var=;
          run;
          

          变成

          data _null_;
              var = "hello";
              put var=;
          
              call symputx('mvVar', 'yes?', 'g');
              var = "hello";
              put var=;
          run;
          

          和打印

          hello
          hello
          

          SAS 还包括一种在数据步内执行宏代码的机制。该机制使用dosubl() 函数或call execute() 例程。该机制是一个鸭带,很难理解,它以非直观的方式工作,绝对不能使用。绝对不会。永远。

          【讨论】:

          • 您对 CALL SYMPUTX() 的符号表选项含义的解释很混乱。 G 选项表示将其写入 GLOBAL 符号表。 L 选项表示将其写入最本地的符号表中。 F 选项(或者不使用第三个参数)意味着写入最局部的现有宏变量,或者如果不存在这样的宏变量,则在最局部的符号表中创建一个新的宏变量。最局部的可能是 GLOBAL 空间,例如在开放代码而不是宏中调用时。
          • 是的,它可能看起来很混乱,但这就是 SAS 的工作原理:如果您设置 G 选项并在本地符号表中有宏变量,那么您在本地符号表中更新变量!我在 SAS 9.4M6 中检查过。
          • 不。如果您使用 G 选项,宏变量的全局版本将被更改(或创建)。 CALL SYMPUTX() 函数调用不会改变 LOCAL 版本的值。
          • 抱歉,再次检查,结果有误。
          • 要想获得更多乐趣(读取不是最本地的宏变量),请查看:github.com/sasutils/macros/blob/master/symget.sas
          猜你喜欢
          • 2019-09-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-12-28
          • 1970-01-01
          • 2021-01-27
          • 2013-12-05
          • 2019-07-09
          相关资源
          最近更新 更多