简体   繁体   中英

SAS - creating macros dynamically

I would like to dynamically create macros to query a transactional data set. I have a table that has a set of parameters ( parameter_data ) and transaction data ( txs ). For each row in my parameter data I want to create a macro that can be called to query the data.

Parameter data:

data parameter_data;
input macro_name $ parameter_name $ parameter_value $;
datalines;
A Person_ID 1
B TX_ID 2
;

Transactional Data:

data txns;
input Person_ID $ TX_ID $ TX_Amount $;
datalines;
John Sales 1123
Mary Acctng 34
John Sales 23
Mary Sales 2134
;

Here I try to create a macro that should create macros dynamically according to the parameter data. The 'inner macros' are the macros that are created from the parameter data.

%macro outerMacro;

/*loop through each row in the parameter table to get the detail of the macro we want to create*/
%DO ROW = 1 %To 2;
data _NULL_;
set parameter_data;
if _N_ = ROW then do;
    call symputx('parameter_name',parameter_name);
    call symputx('parameter_value',parameter_value);
    end;
run;

/*define inner macro parameters*/
%let macroName = myMacro; /*set the name of the macro we want to create*/
%let innerMacroStart = macro &macroName.; /*set the macro name to start the macro definition*/
%let innerMacroEnd = mend &macroName;

%&&innerMacroStart.; /*start the inner macro*/

    /*body of the macro*/
    data output;
    set txns;
    &&parameter_name = &&parameter_value; 
    /*so here effectively for the first row in the parameter table we are filtering where person_id = John*/
    run;

%&&innerMacroEnd.; /*end the inner macro*/

%mend outerMacro;

%&&outerMacroName.;

It seems that SAS is unable to parse the lines %innerMacroStart. Any help is much appreciated.

Thanks!

If the goal is just to subset data then it might be better to generate macro variables instead of actual macros. Try something like this instead.

data _null_;
  set parameter_data ;
  call symputx(macro_name,catx(' ','where also'
              ,parameter_name,'=',quote(trim(parameter_value)),';'));
run;

Then just use the generated where statement(s) when you need them by expanding the macro variable. Like this:

data output ;
  set txns;
  &a 
run;

If you really want to generate a macro definition then you probably want to just use a data step to write the code to a file and then %include the file to compile the macros. That will be much easier to debug than macro logic.

Let's fix your parameter file to better match your test data. Person_ID and TX_ID are character variables in your transaction dataset. You will probably need to add logic or change the parameter file to allow it to handle testing of both numeric and character variables. For now I just made it generate code that assumes that PARAMETER_NAME refers to a character variable so that PARAMETER_VALUE will need to have quotes added to make it a string literal.

data parameter_data;
input macro_name :$32. parameter_name :$32. parameter_value $:200.;
datalines;
A Person_ID John 
B TX_ID Acctng 
;

data txns;
input Person_ID $ TX_ID $ TX_Amount $;
datalines;
John Sales 1123
Mary Acctng 34
John Sales 23
Mary Sales 2134
;

Now let's run a data step to generate the code for all of your macros. I added logic to use AND if there were multiple "parameters" defined for each macro.

filename code temp;
data _null_;
  set parameter_data ;
  by macro_name ;
  file code ;
  if first.macro_name then put
  '%macro ' macro_name ';' 
/ 'data output;'
/ '  set txns;'
/ '  where ' @ 
  ;
  else put ' and ' @ ;
  put parameter_name '=' parameter_value :$quote. @ ;
  if last.macro_name then put
  ';'
/ 'run;'
/ '%mend ' macro_name ';'
  ;
run;

Now just use %include to compile the macros.

%include code / source2 ;

NOTE: %INCLUDE (level 1) file CODE is file C:\...\#LN00048.
432  +%macro A ;
433  +data output;
434  +  set txns;
435  +  where Person_ID ="John" ;
436  +run;
437  +%mend A ;
438  +%macro B ;
439  +data output;
440  +  set txns;
441  +  where TX_ID ="Acctng" ;
442  +run;
443  +%mend B ;
NOTE: %INCLUDE (level 1) ending.

Now you can use your macros.

445   options mprint;
446   %a ;
MPRINT(A):   data output;
MPRINT(A):   set txns;
MPRINT(A):   where Person_ID ="John" ;
MPRINT(A):   run;

NOTE: There were 2 observations read from the data set WORK.TXNS.
      WHERE Person_ID='John';
NOTE: The data set WORK.OUTPUT has 2 observations and 3 variables.

447   %b ;
MPRINT(B):   data output;
MPRINT(B):   set txns;
MPRINT(B):   where TX_ID ="Acctng" ;
MPRINT(B):   run;

NOTE: There were 1 observations read from the data set WORK.TXNS.
      WHERE TX_ID='Acctng';
NOTE: The data set WORK.OUTPUT has 1 observations and 3 variables.

I have placed a comment before each block of code, but essentially it is:

  1. Parameter set up.
  2. Macro generation.
  3. %include.
  4. Call any desired macro.

I have assumed no more than 999 parameter observations - this is controlled by seq .

You can examine file "inner_macro.sas" to see the macro definitions.

NB. When you try it, make sure to use your own path in place of <your-path> (occurs twice):

/* set up parameters */
data parameters;
   infile datalines dlm=',';

   input var      : $8.
         operator : $8.
         value    : $8.
   ;

   datalines;
name,eq,"John"
age,gt,12
weight,eq,0
;

/* read parameters and generate a macro definition for each obs, written to a file */
data _null_;
   file '<your-path>/inner_macro.sas';

   set parameters;

   seq = put(_n_,z3.);

   put '%macro inner_' seq ';';
   put '    where ' var operator value ';';
   put '%mend inner_' seq ';';
   put;
run;

/* %include (submits code in file) all of the macro definitions */
%include '<your-path>/inner_macro.sas';

options mprint;

/* invoke the macro with the required data sets */
data class1;
   set sashelp.class;
   %inner_001;
run;

data class2;
   set sashelp.class;
   %inner_002;
run;

data class3;
   set sashelp.class;
   %inner_003;
run;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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