#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