Assigning a macro variable value to a data step variable in SAS

25,887

Solution 1

No SAS macro actually executes "inside" a data step. The macro language processor and data step compiler as two different subsystems that share the code input stream. They hand off to one another as they "eat" chunks of SAS code. In the case of the original program, the language processor in SAS sees the "data" statement and hands off to the data step compiler. The embedded %test macro call is detected and the code input stream is handed to the macro processor FIRST! The macro processor expands all of the code and macro logic inside of the %test macro and then the whole stream of code is handed back to the SAS data step compiler to compile.

So %test is going to run to completion BEFORE the data step even compiles.

If you are looking to make your own subroutines in data step try proc fcmp. Otherwise, just implement your conditional logic inside of the data step as was suggested.

Solution 2

Re-write it using datastep if/then, not macro if/then, and don't create a macro variable, simply use a datastep variable.

%MACRO TEST(var) ;
  call missing(tempvar) ;
  if --some condition-- then tempvar = --some value-- ;
%MEND ;

data output ;
  set input ;
  %TEST(inp_var) ;
  new_var = tempvar ;
  drop tempvar ;
run ;

Solution 3

You cannot use a macro variable in the same data step where you set it with call symput. The result of your call symput statement is only available after the data step.

So at the time the symget statement is being processed, the macro variable does not exist yet. Also, it seems rather pointless, why don't you use a retain statement to save the value you want?

e.g.:

data output;
    set input;
    retain new_data_step_var;
    if --some condition -- then new_data_step_var = --some value--;
run;

Solution 4

Macros that contain a proc or a data step are not executable inside of a data step. Macros are not functions or subroutines; they are text, just as if you'd typed it out (just saving some time with loops and conditionals). So the contents of your macro need to either be text that could be executed inside a data step:

%macro mymacro(numiters);
*this macro would be easier to do in an array, but it is an example;
%local t;
%do t = 1 to &numiters.;
x&t. = mean(y&t.,z&t.);
%end;
%mend mymacro;

data output;
set input;
%mymacro(5);
run;

In that case, it is easier (and more stylistically correct) to not store a value in a macro variable. Simply contain the result in a data step variable, and if needed pass that variable's name as one of your arguments.

There are also function-style macros, that actually return a value to the data step (or in this case, return text that equates to a value). They can be used on the right side of an equal sign.

%macro xtothey(in,power);
%local t;
&in.
%do t = 1 to &power-1;
*&in.
%end;
;
%mend myfunctionmacro;

data output;
set input;
y = %xtothey(x,4);
run;

That would actually be more easily done in PROC FCMP (which compiles functions and subroutines), but sometimes macros are better for this (or you might not know FCMP well).

Finally, some macros require procs or data steps of their own. In those cases, unless you're using some FCMP elements such as DOSUBL, you will need to store the value somewhere, whether it is in a dataset or a macro. In those cases, you must run the macro prior to the datastep where you want the value - but you only get one (or a finite number of) return values. You don't get one per row unless you go to some extreme lengths, which usually can be done better without using macro variables. I would argue the below is bad form, as you almost always can do it better without using macro variables - but this is how you would do it if you needed to. FCMP with DOSUBL would probably be the superior choice.

%macro findmode(dset,var,outvar);
proc means data=&dset;
var &var.;
output out=_tempset mode(&var.)=&var._mode;
run;
data _null_;
set _tempset;
call symputx("&outvar.",&var._mode);
run;
%mend findmode;

%findmode(sashelp.class,weight,wtmode);
data output;
set input;
mode=&wtmode;
run;
Share:
25,887
athresh
Author by

athresh

A SAS programmer

Updated on September 03, 2020

Comments

  • athresh
    athresh almost 4 years

    I am calling a macro inside a data step and assigning the macro variable to a data step variable as below.

    The input for the macro goes from the input dataset which has some 500 records.

    %macro test(inp_var);
      %global macro_var;
      --- using inp_var variable here---
      %if --some condition-- %then call symput('macro_var',-- some value--);
    %mend;
    
    data output;
      set input;
      %test(inp_var);
      new_data_step_var = symget('macro_var');
    run;
    

    But it's showing the error message pointing the variable new_data_step_var - ERROR 180-322: Statement is not valid or it is used out of proper order.