【问题标题】:Macro numeric values comparing宏数值比较
【发布时间】:2019-01-09 20:48:26
【问题描述】:

我正在尝试比较宏中的两个数值。 但我不断收到以下消息:

ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: 0.2
ERROR: The %TO value of the %DO I loop is invalid.
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: 0.05
ERROR: The %BY value of the %DO I loop is invalid.
ERROR: The macro FAIL will stop executing.

我的代码如下:

%macro fail;
      %do i=0 %to 0.2 %by 0.05;
          data failcrs;
              set fail;
              if f_p>=input(&i, 8.) then output;
          run;
      %end;
%mend failcrs;

f_p 是一个数值变量。

我的代码有什么问题?请帮忙。

非常感谢!

【问题讨论】:

  • 让这个工作很容易,但代码不合逻辑,可能不是你想要的。如果是测试用例,我想没问题。
  • 我没有看到你在哪里比较宏变量?如果要在宏代码中对浮点值进行操作,则需要使用 %sysevalf() 而不是宏语句通常使用的隐式 %eval() 函数调用。 %if %sysevalf( &I >= 0.2) %then ...
  • 谢谢汤姆!我得到了它。另一个宏函数。

标签: macros sas


【解决方案1】:

宏代码(%if%until%while 等)中的条件测试使用仅执行整数运算的 %eval() 宏函数。这包括在 %do-%to-%by 循环中完成的增量和测试。

要使用浮点运算,您需要使用%sysvalf() 宏函数。

您可以将自己的增量编码到循环计数器。

%let i=0;
%do %while( %sysevalf( &I <= 0.2 ) );
   ...
   %let i=%sysevalf(&i + 0.05);
%end;

或者将循环计数器设为整数并使用另一个宏变量来保存分数。

%do j=0 %to 20 %by 5 ;
   %let i=%sysevalf(&j/100);
   ...
%end;

【讨论】:

    【解决方案2】:

    您有几个问题。 宏循环更适用于整数,但一个简单的解决方法是使用 %DO %UNTIL 循环。

    1. %MEND 上的名称与 %MACRO 上的名称不同
    2. %DO %I 循环的值无效。
    3. 非唯一的数据集名称,表示输出会覆盖自身。

      *fake data to work with;  
      data fail;
          do f_p=0 to 0.2 by 0.01;
              output;
          end;
      run;
      
      %macro fail;
          %let i=0;
            %do %until(&i = 0.2); /*2*/
                data failcrs_%sysevalf(&i*100); /*3*/
                    set fail;
                    if f_p>=&i then output;
                run;
                %let i = %sysevalf(&i + 0.05);
            %end;
      %mend fail; /*3*/
      
      *test macro;
      %fail;
      

    cmets 中的数字与发现的问题一致。

    【讨论】:

    • 非常感谢瑞萨!我一直认为任何宏变量的值都是有特征的,即使它被分配了一个数值。嗯,我从哪里得到这样的想法?
    • 我只是想生成的代码会是什么样子以及是否有意义。一般来说,宏变量是文本,而不是数字或字符,如果这样更清楚的话。
    • 我不得不说:不,我还是很困惑。那么文本不是字符吗?您想查看完整的宏代码吗?我可以贴在这里让你看看。也许你可以挑出其他问题。
    • 这个问题真的太长了,无法在 cmets 中回答,并且与您的原始问题无关。生成的代码是input(0.01, 8.),这是无效的,因为 0.01 已经是数字。如果您想让它有效,您也可以通过生成input("0.01", 8.) 来修复它,这在技术上是有效的。无论如何,这不是导致错误的部分,而是 DO 循环,这只是我注意到的另一个问题。
    • 应该是input("&amp;i", 8.)
    【解决方案3】:

    尝试使用 best32。但是,当您的数据集被每个循环覆盖时,您为什么要循环。请检查以下每个步骤的日志。正如 cmets 中的@Reeza 在下面解释的那样,您甚至没有输入语句 选项印记;

       /* do this*/
        %macro fail;
       %let i =15;
        data failcrs;
              set sashelp.class;
    
              if age lt input(&i, best32.)  then output;
          run;
       %mend fail;
    
      %fail
    
       /* dataset overwritten every time to finally pick up 15 as value check in the log*/
       %macro fail1;
       %do i = 1 %to 15;
        data failcrs1;
              set sashelp.class;
    
              if age lt input(i, best32.) then output;
          run;
      %end;
     %mend fail1;
    
    %fail1
    

    【讨论】:

    • 更改了答案,但您的评论本身就是答案。你是对的@Reeza
    • 问题在于 do 循环中的十进制值,这就是产生错误的原因。不确定它们在 %DO 循环中是否有效。
    • 非常感谢大家!我得到了它。所以小数是问题所在。现在代码可以工作了。输出后我会做一些计数和合并,比较长,这里就不贴其余代码了。
    【解决方案4】:
    %macro fail;
            %let i=0;
            %do %until(&i = 0.2);
                data failcrs; 
                    set crse_grade_dist_fail;
                    if f_p>=&i then output;
                    run;
    
            proc sql;
                create table count_failclass as
                select strm, count(class_nbr) as numfclass_%sysevalf(&i*100)
                from failcrs
                group by strm;
            quit;
    
            proc sql;
                create table failfaculty as
                select strm, instructor_id, instructor, count(class_nbr) as numfclass
                from failcrs
                group by strm, instructor_id, instructor;
            quit;
    
            proc sql;
                create table count_failfaculty as
                select strm, count(instructor) as numffaculty_%sysevalf(&i*100)
                from failfaculty
            group by strm;
            quit;
    
            data count_class_faculty;
                set count_class_faculty;
                set count_failclass;
                set count_failfaculty;
            run;
    
            %let i = %sysevalf(&i + 0.05);
        %end;
    %mend fail;
    

    好在我的数据没有 f_p=0,它们都大于零。因为我只想统计失败的课程。

    【讨论】:

    • 不要对浮点数使用相等测试,因为无法准确表示二进制数中的小数部分。 %do %until(%sysevalf(&amp;i &gt;= 0.2));
    • 汤姆,找到你了!我学到了一些技巧。再次感谢您!
    【解决方案5】:

    文档是为阅读而编写的,对“SAS 9.4 Macro Do”的简单搜索应该可以解释这一切 - start、stop 和 by 是整数 - 整数在某种意义上说,无论宏源表达式在它们的位置进行计算还是隐式计算在需要的时候显式地转换成一个整数。

    您编写的宏有点奇怪。它将生成多个数据步骤,这些步骤都会覆盖相同的数据集。您可能希望专注于先不编写宏代码,然后在需要提交重复的样板代码时转向它。编写好的宏代码意味着您必须考虑“这是否会生成适当的源代码以及这些宏语句在其解析范围内会产生什么副作用”

    %DO, Iterative Statement

    语法

    %DO 宏变量=开始 %TO 停止 增量 em>>;

      文本和宏语言语句

    %END;

    必填参数

    宏变量

    • 命名宏变量或生成宏的文本表达式 变量的名称。它的价值作为一个指标来确定 %DO 循环迭代的次数。如果指定了宏变量 由于索引不存在,宏处理器在 局部符号表。

    • 您可以更改 处理过程中的索引变量。例如,使用条件 将索引变量的值设置为超出停止值的处理 满足某个条件时的值结束循环的处理。

    开始停止

    • 指定整数或生成整数的宏表达式 控制宏的部分之间的次数 处理迭代的 %DO 和 %END 语句。

    • 第一次 %DO 组迭代,宏变量等于开始。作为加工 继续,宏变量的值变化的值 递增直到宏变量的值超出 start 和 stop 包含的整数。

    增量

    • 指定一个整数 (除 0 以外)或生成整数的宏表达式 在每次迭代中添加到索引变量的值 环形。默认情况下,增量为 1。增量在 循环的第一次迭代。因此,您不能将其更改为 循环迭代。

    【讨论】:

    • 非常感谢理查德!!
    猜你喜欢
    • 2011-07-31
    • 2019-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-01
    • 2014-04-04
    • 2015-07-29
    • 1970-01-01
    相关资源
    最近更新 更多