Пропустить блок кодов, если макропеременная пуста

#sas #sas-macro

#sas #sas-макрос

Вопрос:

Я создаю макропеременную с приведенным ниже кодом SAS. Он хранит список имен данных, где мне нужно заменить определенные значения в определенных переменных.

 proc sql noprint;
select distinct data_name
into :data_repl separated by ' '
from TP_attribute_matching
where Country="amp;Country_Name" and Replace_this ne ' ';
quit;
  

Я хотел бы пропустить следующие 2 блока, если data_repl пуст. Эти 2 блока просматривают каждый набор данных и переменные в этом наборе данных, а затем заменяют x на y.

 /*Block 1*/
%do i=1 %to %_count_(word=amp;data_repl);
proc sql noprint;
select var_name,
   Replace_this,
   Replace_with
into :var_list_repl_amp;i. separated by ' ',
     :repl_this_list_amp;i. separated by '@',
     :repl_with_list_amp;i. separated by '@'
     from TP_attribute_matching
where Replace_this ne ' ' and data_name="%scan(amp;data_repl,amp;i.)";
quit;

/* Block 2 */
%do i=1 %to %_count_(word=amp;data_repl);
data sasdata.%scan(amp;data_repl,amp;i);
set sasdata.%scan(amp;data_repl,amp;i);
 %do j=1 %to %_count_(word=amp;amp;var_list_repl_amp;i.);
   %let from=%scan("amp;amp;repl_this_list_amp;i.",amp;j,'@');
   %let to=%scan("amp;amp;repl_with_list_amp;i.",amp;j,'@');
   %scan(amp;amp;var_list_repl_amp;i.,amp;j)=translate(%scan(amp;amp;var_list_repl_amp;i.,amp;j),amp;to,amp;from); 
 %end;
run;
%end;
  

Как я должен это сделать? Я просматривал %SKIP и if затем leave, но пока не могу понять это.

Комментарии:

1. Для дальнейшего чтения, технический документ от 2009: support.sas.com/resources/papers/proceedings09/022-2009.pdf , автор рекомендует %if %sysevalf(%superq(param)=,boolean) %then

Ответ №1:

%IF и %DO являются операторами макроса, которые могут использоваться только внутри макроса:

 %macro DoSomething;
%if "amp;data_repl" ne "" %then %do;
    /*Block 1*/
    %do i=1 %to %_count_(word=amp;data_repl);
    proc sql noprint;
    select var_name,
       Replace_this,
       Replace_with
    into :var_list_repl_amp;i. separated by ' ',
         :repl_this_list_amp;i. separated by '@',
         :repl_with_list_amp;i. separated by '@'
         from TP_attribute_matching
    where Replace_this ne ' ' and data_name="%scan(amp;data_repl,amp;i.)";
    quit;

    /* Block 2 */
    %do i=1 %to %_count_(word=amp;data_repl);
    data sasdata.%scan(amp;data_repl,amp;i);
    set sasdata.%scan(amp;data_repl,amp;i);
     %do j=1 %to %_count_(word=amp;amp;var_list_repl_amp;i.);
       %let from=%scan("amp;amp;repl_this_list_amp;i.",amp;j,'@');
       %let to=%scan("amp;amp;repl_with_list_amp;i.",amp;j,'@');
       %scan(amp;amp;var_list_repl_amp;i.,amp;j)=translate(%scan(amp;amp;var_list_repl_amp;i.,amp;j),amp;to,amp;from); 
     %end;
    run;
    %end;

%end;
%mend;

%DoSomething
  

Редактировать:
Вместо проверки строки вы можете использовать count из PROC SQL (amp;SQLOBS macro var)

 %let SQLOBS=0; /* reset SQLOBS */
%let data_repl=; /* initialize data_repl,
                    would not be defined in case when no rows returned */

proc sql noprint;
select distinct data_name
into :data_repl separated by ' '
from TP_attribute_matching
where Country="amp;Country_Name" and Replace_this ne ' '
and not missing(data_name);
quit;

%let my_count = amp;SQLOBS; /* keep the record count from last PROC SQL */
...

%if amp;my_count gt 0 %then %do;
...
...
%end;
  

Если у вас уже есть основной макрос, нет необходимости определять новый (я не уверен, о чем вы спрашиваете сейчас).

Комментарии:

1. Я полагаю, что OP знает это. Я не могу согласиться с синтаксисом, подобным %if "amp;var" = "" , это не очень хороший стиль и приводит к путанице, даже если вы сами знаете, как работают макропеременные в отношении цитирования.

2. Спасибо @vasja, так как мне поместить это в мой основной макрос? %macro main; коды…. %макрос делает что-то; коды… %исправить dosomething; %исправить main; Я пробовал это, но он пропускает весь внутренний макрос или раздел. И иногда amp;data_repl eq или amp;data_repl»ne»» выдает синтаксические ошибки.

3. Это сработало, когда я использовал это: %if amp;data_repl ^= %str() %then %do;

Ответ №2:

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

Во-вторых, способ кодирования этих циклов обычно выглядит примерно так

 %do ... %while amp;macrovar ne ;
  

который проверяет наличие пустого и вообще не выполняет цикл, если он пуст для начала. amp;macrovar был бы результат сканирования. IE:

 %let scan_result = %scan(amp;Data_repl.,1);
%do i = 1 %to %_count_... while amp;scan_result ne ; *perhaps minus one, not sure what %_count_() does exactly;
  ... code
  %let scan_result=%scan(amp;data_Repl.,amp;i 1);
%end;
  

Возвращаясь к обработке списка, то, что вы в конечном итоге делаете, это:

 data amp;dataset.;
 set amp;dataset.;
 [for some set of amp;variables,amp;tos, amp;froms]
 amp;variable. = translate(amp;variable.,amp;to.,amp;from.);
 [/set of variables]
run;
  

Итак, что вам нужно, так это пара макросов. Предполагая, что у вас есть набор данных с

 <dataset> <varname> <to> <from>
  

Вы можете вызвать это довольно легко. Два способа:

Запустите его как набор вложенных макросов / вызовов. Это немного запутаннее, но может быть немного проще для понимания.

  %macro do_dataset(data=);
  proc sql noprint;
    select cats('%convert_Var(var=',varname,',to=',to,',from=',from,')')
      into :convertlist separated by ' '
      from dataset_with_conversions
      where dataset="amp;data.";
  quit;

  data amp;data;
   set amp;data;
   amp;convertlist.;
  run;
 %mend do_dataset;

%macro convert_var(var=,to=,from=);
amp;var. = translate(amp;var.,"amp;to.","amp;from.");
%mend convert_var;

proc sql noprint;
select cats('%do_dataset(data=',dataset,')') 
 into :dslist separated by ' '
 from dataset_with_conversions;
quit;

amp;dslist;
  

Во-вторых, вы можете сделать все это в одном потоке данных, используя call execute (вместо того, чтобы выполнять два разных шага). Т.Е. Выполните by dataset инструкцию, затем для first.dataset выполнения data <dataset>; (заполнение этого) и для last.dataset выполнения run , а в противном случае выполните переводы.

Более сложное, но однопроходное решение — зависит от вашего уровня комфорта, который вы предпочитаете, обычно они должны работать аналогично.

Комментарии:

1. Спасибо, Джо, но do while создает бесконечный цикл, потому что, когда макрос и data_repl возвращают что-то, условие всегда выполняется.

Ответ №3:

если вы хотите пропустить что-то на основе параметра, если data_repl задано как null, вы можете добавить проверку значения, это позволит избежать возникновения ошибки во время инструкции include, поскольку в то время это будет null и может вызвать ошибку. Например, если путь к библиотеке получен на основе переданной переменной. что приведет к недопустимому пути к библиотеке во время инструкции include, мы можем использовать инструкцию skip.

 %macro DoSomething(data_repl=);
   %if "amp;data_repl" ne "" %then %do;
    // your code goes here. 
   %end;
%mend;

%DoSomething