2015-11-20 4 views
6

Я хотел бы вернуть значение из созданного мной макроса SAS, но я не уверен, как это сделать. Макрос вычисляет количество наблюдений в наборе данных. Я хочу, чтобы количество наблюдений было возвращено.SAS - Как вернуть значение из макроса SAS?

%macro nobs(library_name, table_name); 
    proc sql noprint; 
    select nlobs into :nobs 
    from dictionary.tables 
    where libname = UPCASE(&library_name) 
     and memname = UPCASE(&table_name); 
    quit; 

    *return nobs macro variable; 
    &nobs 
%mend; 

%let num_of_observations = %nobs('work', 'patients'); 

Кроме того, я хотел бы переменная &nobs макрос, который используется в макросе быть локальными для этого макроса, а не глобальный характер. Как я могу это сделать?

+0

Мне нравится макрофункция Джек Гамильтона для подсчета обс: http://www2.sas.com/proceedings/sugi26/p095-26.pdf – Quentin

+0

Спасибо за ссылку. Моя главная задача здесь - вернуть значение из макроса. Мне также очень хотелось бы знать, как сделать переменную «в» (в proc sql) локальной, а не глобальной. Это две вещи, которые мне нужно делать постоянно с другими макросами. – bambi

+0

Вы уверены, что вам нужно знать, сколько? Обычно я просто хочу знать ничего или нет - нет. Что вы будете делать с NOBS? –

ответ

2

Вы не можете «вернуть» значение из макрофайла в стиле функции, если только вы не написали его, используя только макрокоманды. Ссылка Квентина дает пример того, как это сделать.

Например, вы не можете использовать макрос, как это так, потому что процедура SQL не может выполняться в середине %put заявления (это возможно с другими более сложными обходами, например dosubl, но не так, как вы написали это) ,

%put %nobs(mylib,mydata); 

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

Чтобы создать макроперемену, которая является локальной для исходного макроса, вы должны сначала объявить ее с помощью инструкции %local в определении макроса.

2

A SAS macro вставляет код. Он никогда не может возвращать значение, хотя в некоторых случаях можно имитировать функции, обычно вам нужно работать вокруг как

%nobs(work, patients, toReturn=num_of_observations) 

** Для того, чтобы помочь вам понять, что происходит, я советую печатая код, вставленный макрос в вашем журнале:;

options mprint; 

Переходит имя макропеременной заполнить макрос, Я считаю, что наиболее практичным

  • не требует от пользователя моего макроса поставить кавычки libary и имена участников
  • введите имя переменной с именованной макропеременной, поэтому мы можем присвоить ей значение по умолчанию;

    % macro nobs (имя_библиотеки, имя_таблицы, toReturn = nobs);

Убедитесь, что переменная для возврата существует

  • Если он существует, как известно за пределами этого макроса.
  • Otherwisse, если мы создадим его здесь, он по умолчанию будет локальным и потерян, когда мы покинем макрос;

    %if not %symexist(&toReturn.) %then %global &toReturn.; 
    

В SQL я

  • использовать SASHELP.VTABLE, вид предоставляемой SAS на своих метаданных
  • добавить кавычки я опущенные в макровызове («", not '': макропеременные не заменяются в одиночных qoutes)
  • использовать функцию макросъемки макросов% вместо функции ожидания SAS, так как это иногда повышает производительность;

    proc sql noprint; 
        select nobs 
        into :&toReturn. 
        from sashelp.vtable 
        where libname = %UPCASE("&library_name.") 
        and memname = %UPCASE("&table_name."); 
    quit; 
    

    % починка;

Обратите внимание, если вы звоните макрос внутри макроса, Выполнив этот код и читать журнал, чтобы понять, почему;

%macro test_nobs(); 
    %nobs(sashelp, class); ** will return the results in nobs **; 

    %nobs(sashelp, shoes, toReturn=num_of_shoes); 

    %let num_of_cars = ; 
    %nobs(sashelp, cars, toReturn=num_of_cars); 

    %put NOTE: inside test_nobs, the following macro variables are known; 
    %put _user_; 
%mend; 

%test_nobs; 
%put NOTE: outside test_nobs, the following macro variables are known; 
%put _user_; 
+0

Может ли тот, кто проголосовал за меня, прошу объяснить. Разве это не работает для вас? –

+0

'Твас и я извиняюсь из-за отсутствия комментариев. В редакции добавлен полезный контент. Но первые два предложения фактически неверны. Макрос может вернуть значение, поэтому% let num_of_observations =% nobs («работа», «пациенты»); может работать. Это преимущество макросов функционального стиля, таких как представленный в статье Джека Гамильтона, который имеет макрос в стиле функции для подсчета наблюдений. Таким образом, ответ по-прежнему ошибочен. – Quentin

+0

Спасибо, что указал на пример Гамильтона, Квентин. Однако он все еще просто вставляет символы в предварительно скомпилированный код. Он решает вопрос, который дал бамби в качестве примера, но не фундаментальный. –

3

Что такое проблема написания функции, как макросы?

т.е. макросы можно использовать в качестве %let myVar = %myMacro(myArgument)

  • Вы можете использовать написанный пользователем макрос, как если бы это была функция, если вы все это
    • вызова некоторых %doSomething(withSometing) как макро функций
    • присваивать значения макропеременным с помощью оператора %let someVar =
    • «вернуть» ваш результат, как правило, путем написания &myResult. на последней строке перед вашим %mend
  • Как только вы включаете proc или data шаг в макросе, это не работает больше
  • К счастью,% sysFunc() приходит на помощь, поэтому мы можно использовать любой шаг данные функции
  • Это включает в себя низкие функции уровня, как open, fetch и close, которые могут даже получить доступ к данным
  • NERDY люди могут сделать довольно много с ним, но даже если вы всезнайка, ваш босс будет редко дайте вам время для этого.

Как это решить?, т. Е. Какие строительные блоки я использую для решения этой проблемы?

  • proc fcmp позволяет упаковывать некоторые операторы ступенчатые данных в подпрограмме или функции
  • Эта функция, предназначенная для использования на этапе данных, могут быть использованы в %sysfunc()
  • В этой функции вы можете позвонить run_macro выполнить любой макрос в справочном НЕМЕДЛЕННО

Теперь мы готовы к практическому решению

Шаг 1: написать вспомогательный макрос

  • без параметров,
  • с использованием некоторых глобальных переменных макросов
  • «возвращение» свой результат в глобальной переменной макро

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

** macro nobsHelper retrieves the number of observations in a dataset 
    Uses global macro variables: 
     nobsHelper_lib: the library in which the dataset resides, enclosed in quotes 
     nobsHelper_mem: the name of the dataset, enclosed in quotes 
    Writes global macro variable: 
     nobsHelper_obs: the number of observations in the dataset 

    Take care nobsHelper exists before calling this macro, or it will be ost 
**; 
%macro nobsHelper(); 
    ** Make sure nobsHelper_obs is a global macro variable**; 
    %global nobsHelper_obs; 

    proc sql noprint; 
     select nobs 
     into :nobsHelper_obs 
     from sashelp.vtable 
     where libname = %UPCASE(&nobsHelper_lib) 
      and memname = %UPCASE(&nobsHelper_mem); 
    quit; 
    %* uncomment these put statements to debug **; 
    %*put NOTE: inside nobsHelper, the following macro variables are known; 
    %*put _user_; 
%mend; 

Шаг 2: написать вспомогательную функцию;

**Functions need to be stored in a compilation library; 
options cmplib=sasuser.funcs; 

** function nobsHelper, retrieves the number of observations in a dataset 
    Writes global macro variables: 
     nobsHelper_lib: the library in which the dataset resides, enclosed in quotes 
     nobsHelper_mem: the name of the dataset, enclosed in quotes 
    Calls the macro nobsHelper 

    Uses macro variable: 
     nobsHelper_obs: the number of observations in the dataset 
**; 
proc fcmp outlib=sasuser.funcs.trial; 
    ** Define the function and specity it should be called with two character vriables **; 
    function nobsHelper(nobsHelper_lib $, nobsHelper_mem $); 
     ** Call the macro and pass the variables as global macro variables 
     ** The macro variables will be magically qouted **; 
     rc = run_macro('nobsHelper', nobsHelper_lib, nobsHelper_mem); 
     if rc then put 'ERROR: calling nobsHelper gave ' rc=; 

     ** Retreive the result and pass it on **; 
     return (symget('nobsHelper_obs')); 
    endsub; 
quit; 

Шаг 3: написать удобство макрос для использования помощников;

** macro nobs retrieves the number of observations in a dataset 
    Parameters: 
     library_name: the library in which the dataset resides 
     member_name: the name of the dataset 
    Inserts in your code: 
     the number of observations in the dataset 
    Use as a function 
**; 
%macro nobs(library_name, member_name); 
    %sysfunc(nobsHelper(&library_name, &member_name)); 

    %* Uncomment this to debug **; 
    %*put _user_; 
%mend; 

Наконец использовать его;

%let num_carrs = %nobs(sasHelp, cars); 
%put There are &num_carrs cars in sasHelp.Cars; 

Data aboutClass; 
    libname = 'SASHELP'; 
    memname = 'CLASS'; 
    numerOfStudents = %nobs(sasHelp, class); 
run; 

Я знаю, что это сложный но, по крайней мере, все ботаны работа. Вы можете копировать, вставлять и изменять это за время, которое примет ваш босс. ;

+1

Ах, решение FCMP/RUN_MACRO - собиралось написать это отдельно, приятно видеть, что вы уже это сделали. Посмотрите вперед к комментариям. – Joe

+1

Да, это хороший подход, что Майк Роадс назвал «Сэндвич с макросъемкой» http://support.sas.com/resources/papers/proceedings12/004-2012.pdf. Поэтому теперь, возможно, кто-то добавит подобное решение DOSUBL, например. https://support.sas.com/resources/papers/proceedings13/032-2013.pdf. У меня есть оговорки о DOSUBL из-за возможности столкновения с областью, но моя доброта выглядит очень красиво. Так что, надеюсь, он будет усовершенствован в будущих выпусках. – Quentin

+0

Спасибо за вызов, Бамби! Джо, Квентин и я наслаждались этим! –

7

Я отвечу на основной вопрос Бемби спросил в комментариях:

Моя главная задача здесь, как вернуть значение из макроса.

Я собираюсь сыграть важную роль с Дирк. Он говорит:

A SAS macro inserts code. Она никогда не может возвращать значение, хотя в некоторых случаях вы можете имитировать функции

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

Однако, макрос может возвращать только одно значение , если он имеет только макро заявления в дополнение к этому значению. Значение, каждая строка должна начинаться с %. Все, что не начинается с %, будет возвращено (и некоторые вещи, которые начинаются с %, также могут быть возвращены).

Так важный вопрос, Как вернуть только значение из макроса.


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

Связанный с Джеком Гамильтоном paper содержит пример, который здесь подходит. Он отклоняет этот пример, но это во многом потому, что его статья посвящена подсчету наблюдений в случаях, когда NOBS ошибочен - либо с предложением WHERE, либо в некоторых других случаях, когда наборы данных были изменены без обновления метаданных NOBS.

В вашем случае вы, кажется, счастливы доверять NOBS - так этот пример будет делать.

Макрос, который возвращает значение должно быть точно один о том, что либо это не макрос заявление синтаксиса, или является макрос заявление синтаксис, который возвращает значение в поток обработки. %sysfunc - пример инструкции, которая делает это. Такие вещи, как %let, %put, %if и т. Д. Являются инструкциями синтаксиса, которые ничего не возвращают (сами по себе); поэтому вы можете иметь столько, сколько хотите.

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

Вот урезанная версия макроса Джека в конце страницы 3, упрощенно удалить nlobsf, что он показывает неправильно:

%macro check; 

    %let dsid = %sysfunc(open(sashelp.class, IS)); 
    %if &DSID = 0 %then 
    %put %sysfunc(sysmsg()); 

    %let nlobs = %sysfunc(attrn(&dsid, NLOBS)); 

    %put &nlobs; 

    %let rc = %sysfunc(close(&dsid)); 

%mend; 

Этого макрос не функции стиль макросов. Он ничего не возвращает в поток обработки! Это полезно для просмотра журнала, но не полезно для предоставления вам значения, с которым вы можете запрограммировать. Тем не менее, это хороший старт для макроса стиля функции, потому что вы действительно хотите, чтобы &nlobs, правильно?

%macro check; 

    %let dsid = %sysfunc(open(sashelp.class, IS)); 
    %if &DSID = 0 %then 
    %put %sysfunc(sysmsg()); 

    %let nlobs = %sysfunc(attrn(&dsid, NLOBS)); 

    &nlobs 

    %let rc = %sysfunc(close(&dsid)); 

%mend; 

Теперь это функция стиль макроса: он имеет одно утверждение, которое не является макро заявление синтаксис, &nlobs. на простой линии все само по себе.

Это на самом деле больше, чем вам нужно одним утверждением; помните, как я сказал, что %sysfunc возвращает значение в поток обработки?Вы можете удалить %let часть этого заявления, оставив вас с

%sysfunc(attrn(&dsid, NLOBS)) 

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

Давайте хорошо себя и добавить несколько %local с, чтобы получить это красиво и безопасно, и сделать имя набора данных параметра, потому что природа не приемлет макрос без параметров:

%macro check(dsetname=); 

    %local dsid nlobs rc; 

    %let dsid = %sysfunc(open(&dsetname., IS)); 
    %if &DSID = 0 %then 
    %put %sysfunc(sysmsg()); 

    %let nlobs = %sysfunc(attrn(&dsid, NLOBS)); 

    &nlobs 

    %let rc = %sysfunc(close(&dsid)); 

%mend; 

%let classobs= %check(dsetname=sashelp.class); 

%put &=classobs; 

Там у вас есть : макрос стиля функции, который использует функцию nlobs, чтобы узнать, сколько строк находится в любом конкретном наборе данных.

+3

Gosh, @Joe, я пришел, чтобы написать ответ, и нашел, что вы уже его записали, поэтому я должен буду ждать в следующий раз. Тем не менее, я бы удалил точку с запятой, которую возвращает ваш макрос: '&nlobs;'. Обычно функция макросов возвращает только значение без точки с запятой, чтобы его можно было вызывать в середине оператора. Я большой поклонник простой макросы Питера Кроуфорда% NOW и его макро-функция, которая описывает ее: http://www2.sas.com/proceedings/sugi31/038-31.pdf. Понимание макрофункций изменило мое понимание макроязыка. – Quentin

+0

@Quentin Хорошая точка, которая не должна иметь ';' в ней - исправлена. – Joe

+0

ОК, Джо, если вы так видите, все макросы - это функции. Это все или нет. –

Смежные вопросы