Я хотел бы динамически создавать макросы для запроса набора транзакционных данных. У меня есть таблица с набором параметров ( parameter_data ) и данными транзакции ( txs ). Для каждой строки в моих данных параметров я хочу создать макрос, который можно вызвать для запроса данных.

Данные параметра:

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

Транзакционные данные:

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

Здесь я пытаюсь создать макрос, который должен динамически создавать макросы в соответствии с данными параметра. «Внутренние макросы» - это макросы, которые создаются из данных параметров.

%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.;

Похоже, что SAS не может проанализировать строки% innerMacroStart. Любая помощь очень ценится.

Спасибо!

0
rich 8 Окт 2018 в 10:47

2 ответа

Лучший ответ

Если цель состоит только в подмножестве данных, может быть лучше сгенерировать макрос переменные вместо фактических макросов. Попробуйте вместо этого что-нибудь подобное.

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

Затем просто используйте сгенерированные операторы where, когда они вам нужны, расширяя макропеременную. Как это:

data output ;
  set txns;
  &a 
run;

Если вы действительно хотите сгенерировать определение макроса, то вы, вероятно, захотите просто использовать шаг данных для записи кода в файл, а затем% включить файл для компиляции макросов. Это будет намного проще отладить, чем логику макросов.

Давайте исправим ваш файл параметров, чтобы он лучше соответствовал вашим тестовым данным. Person_ID и TX_ID - символьные переменные в вашем наборе данных транзакции. Возможно, вам потребуется добавить логику или изменить файл параметров, чтобы он мог обрабатывать тестирование как числовых, так и символьных переменных. На данный момент я просто заставил его сгенерировать код, который предполагает, что PARAMETER_NAME относится к символьной переменной, поэтому для PARAMETER_VALUE нужно будет добавить кавычки, чтобы сделать его строковым литералом.

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
;

Теперь давайте запустим шаг данных, чтобы сгенерировать код для всех ваших макросов. Я добавил логику для использования AND, если для каждого макроса было определено несколько «параметров».

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;

Теперь просто используйте% include для компиляции макросов.

%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.

Теперь вы можете использовать свои макросы.

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.
0
Tom 8 Окт 2018 в 15:39

Я поместил комментарий перед каждым блоком кода, но по сути это:

  1. Параметр настроен.
  2. Макрогенерация.
  3. %включают.
  4. Вызов любого желаемого макроса.

Я предположил, что не более 999 наблюдений параметров - это контролируется seq.

Вы можете изучить файл "inner_macro.sas", чтобы увидеть определения макросов.

NB. Когда вы попробуете, убедитесь, что используете ваш собственный путь вместо <your-path> (встречается дважды):

/* 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;
0
Amir 8 Окт 2018 в 15:11