web-dev-qa-db-ja.com

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マクロ内で使用され、そのマクロに対してローカルであり、グローバルではないマクロ変数。どうやってやるの?

12
bambi

バンビがコメントで尋ねた中心的な質問に答えます:

ここでの主な関心事は、マクロから値を返す方法です。

ここで、ダークと一緒に重要な方法でごまかします。彼は言う:

A SASマクロはコードを挿入します。場合によっては関数を模倣できますが、値を返すことはできません

同意しません。 A SAS macro returns処理ストリームに挿入されるテキスト。Returnsはそのための適切な用語です。そして、テキストがたまたま単一の数値の場合、 値を返すと言ってもかまいません。

ただし、マクロは単一の値のみを返すことができますその値に加えてマクロステートメントしかない場合。つまり、すべての行は%で始まる必要があります。 %で始まらないものはすべて返されます(%で始まるものも返される場合があります)。

したがって重要な質問は、[マクロからの値を返すのみどうすればよいですかです。


この場合のように、onlyマクロコードで完全に可能である場合もあります。実際、多くの場合、これは技術的に可能です。ただし、多くの場合、必要以上に多くの作業が必要です。

ジャックハミルトンのリンク paper には、ここで適切な例が含まれています。彼はこの例を却下しますが、それは主に、彼の論文が観測値のカウントについてであるということですNOBSが間違っている場合-WHERE句を使用するか、NOBSメタデータがなくてもデータセットが変更された特定の場合更新しました。

あなたの場合、あなたはNOBSを信頼して完全に満足しているように見えます-したがって、この例はそうします。

値を返すマクロには、exactlyマクロ構文ステートメントではない1つのステートメント、またはis値を処理ストリームに返すマクロ構文ステートメントが必要です。 %sysfuncは、そのようにするステートメントの例です。 %let%put%ifなどのようなものは、(それ自体では)何も返さない構文ステートメントです。必要に応じてそれらをいくつでも持つことができます。

また、haveを使用して、処理ストリームに値を配置する1つのステートメントを作成します。そうしないと、マクロから何も得られません。

以下は、ページ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;

そのマクロはnot関数スタイルのマクロです。処理ストリームには何も返しません!ログを確認するのに役立ちますが、プログラムに使用できる値を提供するのには役立ちません。ただし、実際に必要なのは&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;

Nowこれは関数スタイルのマクロです。マクロ構文ステートメントではない1つのステートメント&nlobs.が1行にあります。

実際には、1つのステートメントで必要とする以上のものです。 %sysfuncが処理ストリームに値を返すと言ったことを覚えていますか?そのステートメントの%letの部分を削除して、

 %sysfunc(attrn(&dsid, NLOBS))

そして、値は処理ストリーム自体に直接配置されます-直接使用することができます。もちろん、何か問題が発生した場合のデバッグは簡単ではありませんが、必要に応じて回避できると確信しています。また、ステートメントの最後にセミコロンがないことに注意してください。これは、マクロ関数を実行するためにセミコロンが不要であり、無関係なセミコロンを返したくないためです。

自然にパラメータのないマクロが嫌われるため、これを適切に実行し、いくつかの%localsを追加して、これを適切かつ安全に取得し、データセットの名前をパラメータにします。

 %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関数を使用する関数スタイルマクロができました。

11
Joe

関数のようなマクロを書く際の問題は何ですか?

つまり、as%let myVar = %myMacro(myArgument)を使用できるマクロ

  • ユーザーが作成したマクロを、あたかも関数であるかのように使用できます
    • マクロ関数のような%doSomething(withSometing)を呼び出す
    • _%let someVar =_ステートメントを使用してマクロ変数に値を割り当てる
    • 結果を「返す」。通常、_&myResult._の前の最後の行に_%mend_と書き込みます。
  • マクロにprocまたはdataステップを含めるとすぐに、これは機能しなくなります
  • 幸い、%sysFunc()が役に立ちますので、任意のデータステップ関数を使用できます
  • これには、データにアクセスできるopenfetchcloseなどの低レベル関数が含まれます
  • オタクの人はそれでかなり多くのことができますが、たとえあなたがオタクであっても、上司があなたにそうする時間を与えることはめったにありません。

これをどのように解決しますか?、つまり、これを解決するためにどのビルディングブロックを使用しますか?

  • _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;
_

私はこれが複雑であることを知っていますしかし、少なくともすべてのオタクな仕事は行われています。上司が受け入れる時間内にこれをコピー、貼り付け、変更できます。 ;

9
Dirk Horsten

A SASマクロはコードを挿入します。値を返すことはできませんが、場合によっては関数を模倣できます通常は回避策が必要です =好き

%nobs(work, patients, toReturn=num_of_observations )

**何が起こるかを理解しやすくするために、マクロによって挿入されたコードをログに出力することをお勧めします。

options mprint;

マクロに入力するマクロ変数の名前を渡します

  • ライブラリとメンバー名を引用符で囲む必要がない私のマクロのユーザー
  • 変数の名前を名前付きマクロ変数にして、デフォルトを指定できるようにします。

    %macro nobs(library_name、table_name、toReturn = nobs);

返す変数が存在することを確認してください

  • 存在する場合は、このマクロの外で認識されます。
  • ここで作成すると、デフォルトでローカルになり、マクロを離れると失われます。

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

SQLの場合、I

  • sASHELP.VTABLEを使用します。これは、メタデータに対してSASによって提供されるビューです
  • マクロ呼び出しで省略した引用符を追加します(「」ではなく「」:マクロ変数は単一のqoutで置換されません)
  • SAS upcase関数の代わりにマクロ%upcase関数を使用します。これにより、パフォーマンスが向上する場合があります。

    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_;
3
Dirk Horsten

マクロステートメントのみを使用して記述していない限り、関数スタイルマクロから値を「返す」ことはできません。クエンティンのリンクは、これを行う方法の例を提供します。

たとえば、proc sqlは%putステートメントの途中で実行できないため、このようにマクロを使用することはできません(これは、dosublなどの他のより複雑な回避策では可能ですが、それを書いた)。

%put %nobs(mylib,mydata);

大きな変更を加えずにできる最善の方法は、グローバルマクロ変数を作成し、それを後続のステートメントで使用することです。

元のマクロに対してローカルなマクロ変数を作成するには、まずマクロ定義内の%localステートメントを介してそれを宣言する必要があります。

2
user667489

私はこの議論に非常に遅れていることを知っていますが、これに遭遇したのでコメントすることを考えました。これはこれを行う別の方法だと思います:

%macro get_something_back(input1, input2, output);
  &output = &input1 + &input2;
%mend;

data _test_;
  invar1 = 1; invar2 = 2;
  %get_something_back(invar1, invar2, outvar);
end;

これはデータステップ以外でも機能します。


%global sum;

%macro get_something_back(input1, input2, outvar);
  %let &outvar = &sysevalf(&input1 + &input2);
%mend;

%get_something(1, 2, sum);

0
Python R SAS