условно выполнить макро функцию - PullRequest
0 голосов
/ 25 июня 2019

Я хочу условно выполнить макрофункцию в зависимости от существования таблицы

Данные

data have;
    do i = 1 to 5;
        output;
    end;
run;

Таблица, которую я хочу выполнить для

data counttbl;
    infile datalines delimiter='::'; 
    format variable $char400. condition $char400.;
    input variable $ condition $;
    datalines;
runcount::i>1
;
run;

Некоторые тесты, чтобы показатьчто я могу обусловить существование counttbl (работает как положено)

data _null_;
    call execute("data want;");
    call execute("set have;");
    if exist("work.counttbl") then
        call execute("tmp = 1;");
    call execute('run;');
stop;
run;

Приведенный выше создает столбец tmp = 1

proc delete data=work.counttbl;
run;

data _null_;
    call execute("data want;");
    call execute("set have;");
    if exist("work.counttbl") then
        call execute("tmp = 1;");
    call execute('run;');
stop;
run;

После удаления таблицы, выше не создаетколонка tmp

Макро-функция для выполнения

%macro apply_change();
    call execute('if _n_ eq 1 then do;');
    do until (eof);
        set counttbl (keep=variable) end=eof;
        call execute(strip(variable) || ' = 0;');
    end;
    call execute('end;');
    call missing(eof);
    do until (eof);
        set counttbl end=eof;
        call execute(strip(variable) || ' + (' || strip(condition) || ');');
    end;
    call missing(eof);
%mend apply_change;

Работает нормально, когда counttbl существует

data _null_;
    call execute("data want;");
    call execute("set have;");
    if exist("work.counttbl") then
        %apply_change()
    call execute('run;');
stop;
run;

Выдает ошибку при удалении counttbl - хочуэто просто пропустить выполнение макрофункции

proc delete data=work.counttbl;
run;

data _null_;
    call execute("data want;");
    call execute("set have;");
    if exist("work.counttbl") then
        %apply_change()
    call execute('run;');
stop;
run;

ERROR: File WORK.COUNTTBL.DATA does not exist.

Заранее спасибо за помощь

Ответы [ 3 ]

2 голосов
/ 25 июня 2019

Ваша проблемная область:

if exist("work.counttbl") then
    %apply_change()

Макрос обрабатывается и генерирует исходный код до того, как система SAS неявно скомпилирует шаг данных и выполнит его.

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

Если вы сохраните, есть пара советов

  • Поместите все генерации кода в макрос
  • поместите экзистенциальную проверку в макрос и НЕ кодируйте исходный код, имеющий SET counttbl, когда его нет

Например:

%macro apply_change();
    %if not %sysfunc(EXISTS(WORK.COUNTTBL)) %then %return; 

    %* This code gen only done when COUNTTBL present;
    call execute('if _n_ eq 1 then do;');
    do until (eof);
        set counttbl (keep=variable) end=eof;
        call execute(strip(variable) || ' = 0;');
    end;
    call execute('end;');
    call missing(eof);
    do until (eof);
        set counttbl end=eof;
        call execute(strip(variable) || ' + (' || strip(condition) || ');');
    end;
    call missing(eof);
%mend apply_change;

Заменить

if exist("work.counttbl") then %apply_change()

С

%apply_change()
1 голос
/ 25 июня 2019

Первая

if exist("work.counttbl") then

будет применяться только к первой строке вашего макроса

 call execute('if _n_ eq 1 then do;');

Это потому, что макрос оценивается до выполнения шага данных.Таким образом, sas просто вставит содержимое макроса в место вызова макроса.Но даже если он будет применяться ко всему макросу, он не будет работать.Возьмем для примера следующий код:

data x;
  if 0 then do;
    set y;
  end;
  set z;
run;

Ее y и z должны существовать.Однако из y не будет считано никаких наблюдений. Только структура взята.

1 голос
/ 25 июня 2019

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

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

Похоже, вы хотите, чтобы новый набор данных был сгенерирован независимо от того, существует файл со списком пар VARIABLE / CONDITION или нет. Так что просто запрограммируйте ту часть шага данных и только условно сгенерируйте ту часть, которая вычисляет новые переменные. Поскольку вы генерируете операторы сумм, нет необходимости в блоке IF _N_ = 1 устанавливать нулевые начальные значения. SAS автоматически установит их на ноль и сохранит их. (Предполагая, что в HAVE еще нет переменных с этими именами, в которых оператор sum также не будет работать правильно.)

filename code temp;
data _null_;
  file code ;
%if %sysfunc(exist(&dsname)) %then %do;
  set &dsname end=eof;
  put '  ' variable '+ ( ' condition ');' ;
%end ;
run;

Таким образом, либо временный файл CODE пуст, либо имеет такой код:

VAR1 + ( dx='123' );
VAR2 + ( sex='M' );

Затем, чтобы создать набор данных, просто запустите этот шаг с %INCLUDE, чтобы добавить условно сгенерированный код.

data want;
  set have;
%include code /source2;
run;

Если вы используете старую версию SAS, вам нужно заключить этот оператор %IF в макрос. Но новейшие выпуски SAS допускают этот тип простой конструкции% IF /% THEN /% DO /% END в открытом коде.

...