简体   繁体   English

在数据步骤中调用宏并将其值分配给变量

[英]Invoking a macro in data step and assigning its value to a variable

I would wish to invoke a macro inside my data step for determining what value should the new variable, that I am creating, receive.我希望在我的数据步骤中调用一个宏来确定我正在创建的新变量应该接收什么值。

I know that I don't necessarily need a macro for this, that I can just include the code in the data step.我知道我不一定需要一个宏,我可以在数据步骤中包含代码。 The problem is that the problem I am working with is quite convoluted, and there are more than 5 similar if-else statements.问题是我正在处理的问题非常复杂,并且有超过 5 个类似的 if-else 语句。 However, essentially only thing that changes is what goes inside the macro function.然而,本质上唯一改变的是宏函数内部的内容。

Hence the need for the macro.因此需要宏。 Below is the code and the error log.下面是代码和错误日志。 I believe the problem should not be in the macro, I have tested it and it succesfully works.我认为问题不应该出在宏上,我已经对其进行了测试,并且成功运行。 For some reason, though, it does not when invoking it in data step with reference to a variable and a local macro variable.但是,出于某种原因,在参考变量和局部宏变量的数据步骤中调用它时不会。

I read that it is because one should use %sysfunc or %execute call sort of things but I cannot use sysfunc with self-defined macros and execute call also didn't work, or at least I couldn't get it to work, because I want to assign the value of that macro to a temp local variable.我读到这是因为应该使用 %sysfunc 或 %execute call 之类的东西,但是我不能将 sysfunc 与自定义宏一起使用并且执行调用也不起作用,或者至少我无法让它工作,因为我想将该宏的值分配给临时局部变量。

IE something like %let temp = execute call(CalculatePayment(args)) but this doesn't work. IE 类似于 %let temp = execute call(CalculatePayment(args)) 但这不起作用。

%let preliminary_1 = 288;
%let preliminary_2 = 115;
%let preliminary_3 = 58;

data __temp__;
set sashelp.cars;
run;


%macro CalculatePayment(initialPayment, paymentLimit);

    %let temp = %sysfunc(min(&initialPayment,&paymentLimit));
    %let candidate = %eval(&temp. - 27);

    %if( &candidate >= 0) %then %let rValue =  &temp;
    %else %let rValue = 0;
    &rValue
%mend;


data _temp_;
set __temp__;
if weight <= 100 then do; 
    preliminary = 0;
end;

else if (find(Model,"2")) then do;
    %let fstCase = %CalculatePayment(weight,&preliminary_1);
    %let seCase = %CalculatePayment(weight,&preliminary_1);
    preliminary = %eval(&fstCase. + &seCase.);
end;
/*
else if (find(Model,"g") & find(Model,"1")) then do;
    %let fstCase = %CalculatePayment(weight,&preliminary_1);
    %let seCase = %CalculatePayment(0.2 * weight,&preliminary_2);
    %let thirCase = %CalculatePayment(0.8 * weight,&preliminary_3);
    preliminary = %eval(&fstCase. + &seCase. + &thirCase.);
end;
*MORE SIMILAR IF-STATEMENTS BELOW with the values to the macro changing;
*/
else do; preliminary = 0; 
end;

run;

Note here is a small snippet of the error log.注意这里是错误日志的一小段。 I did not put it here fully because you can reproduce the same exact log by just running the code.我没有把它完全放在这里,因为你可以通过运行代码来重现完全相同的日志。

ERROR LOG:错误日志:

54          %let fstCase = %CalculatePayment(weight,&preliminary_1);
ERROR: Argument 1 to function MIN referenced by the %SYSFUNC or %QSYSFUNC macro function is not a number.
ERROR: Invalid arguments detected in %SYSCALL, %SYSFUNC, or %QSYSFUNC argument list.  Execution of %SYSCALL statement or %SYSFUNC 
       or %QSYSFUNC function reference is terminated.
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: 
       . - 27 
ERROR: The macro CALCULATEPAYMENT will stop executing.
55          %let seCase = %CalculatePayment(weight,&preliminary_1);
ERROR: Argument 1 to function MIN referenced by the %SYSFUNC or %QSYSFUNC macro function is not a number.
ERROR: Invalid arguments detected in %SYSCALL, %SYSFUNC, or %QSYSFUNC argument list.  Execution of %SYSCALL statement or %SYSFUNC 
       or %QSYSFUNC function reference is terminated.
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: 
       . - 27 
ERROR: The macro CALCULATEPAYMENT will stop executing.
56          preliminary = %eval(&fstCase. + &seCase.);
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: 
       + 
56          preliminary = %eval(&fstCase. + &seCase.);
                                                     _
                                                     22
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, 
              a missing value, INPUT, PUT.  
    

Two issues with your approach.你的方法有两个问题。

First is that the macro processor does its work before the code is sent to SAS itself to be compiled and run.首先是宏处理器在代码被发送到 SAS 本身进行编译和运行之前完成它的工作。 %LET statements in the middle of data step are going to execute before the data step is even compileed.数据步骤中间的 %LET 语句将在数据步骤编译之前执行。 Inserting %LET statements there is just going to confuse the programmer.在那里插入 %LET 语句只会让程序员感到困惑。

Second is calculations involving data should be done with SAS code, not macro code.其次是涉及数据的计算应该用 SAS 代码完成,而不是宏代码。

You can use macro code to help you generate the SAS code you need to run.您可以使用宏代码来帮助您生成运行所需的 SAS 代码。

Looks like you are trying to do something like this.看起来你正在尝试做这样的事情。

First create a macro that generates statements that will work in a data step.首先创建一个宏来生成将在数据步骤中工作的语句。

%macro CalculatePayment(initialPayment, paymentLimit, varname);
    temp = min(&initialPayment,&paymentLimit);
    if (temp - 27) >= 0 then &varname = temp;
    else &varname = 0 ;
    drop temp;
%mend;

Now you can call that macro inside your data step and the generated statements will become part of the data step.现在您可以在数据步骤中调用该宏,生成的语句将成为数据步骤的一部分。

%let preliminary_1 = 288;
%let preliminary_2 = 115;
%let preliminary_3 = 58;
options mprint;
data _temp_;
  set sashelp.cars ;
  if weight <= 100 then preliminary = 0;
  else if (find(Model,"2")) then do;
    %CalculatePayment(weight,&preliminary_1,fstCase);
    %CalculatePayment(weight,&preliminary_1,seCase);
    preliminary = fstCase + seCase;
  end;
  else preliminary = 0; 
run;

The MPRINT option will show you the SAS code your macro has generated for you. MPRINT 选项将显示您的宏为您生成的 SAS 代码。

1082  options mprint;
1083  data _temp_;
1084    set sashelp.cars ;
1085    if weight <= 100 then preliminary = 0;
1086    else if (find(Model,"2")) then do;
1087      %CalculatePayment(weight,&preliminary_1,fstCase);
MPRINT(CALCULATEPAYMENT):   temp = min(weight,288);
MPRINT(CALCULATEPAYMENT):   if (temp - 27) >= 0 then fstCase = temp;
MPRINT(CALCULATEPAYMENT):   else fstCase = 0 ;
MPRINT(CALCULATEPAYMENT):   drop temp;
1088      %CalculatePayment(weight,&preliminary_1,seCase);
MPRINT(CALCULATEPAYMENT):   temp = min(weight,288);
MPRINT(CALCULATEPAYMENT):   if (temp - 27) >= 0 then seCase = temp;
MPRINT(CALCULATEPAYMENT):   else seCase = 0 ;
MPRINT(CALCULATEPAYMENT):   drop temp;
1089      preliminary = fstCase + seCase;
1090    end;
1091    else preliminary = 0;
1092  run;

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

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