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
マクロ内で使用され、そのマクロに対してローカルであり、グローバルではないマクロ変数。どうやってやるの?
バンビがコメントで尋ねた中心的な質問に答えます:
ここでの主な関心事は、マクロから値を返す方法です。
ここで、ダークと一緒に重要な方法でごまかします。彼は言う:
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))
そして、値は処理ストリーム自体に直接配置されます-直接使用することができます。もちろん、何か問題が発生した場合のデバッグは簡単ではありませんが、必要に応じて回避できると確信しています。また、ステートメントの最後にセミコロンがないことに注意してください。これは、マクロ関数を実行するためにセミコロンが不要であり、無関係なセミコロンを返したくないためです。
自然にパラメータのないマクロが嫌われるため、これを適切に実行し、いくつかの%local
sを追加して、これを適切かつ安全に取得し、データセットの名前をパラメータにします。
%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
関数を使用する関数スタイルマクロができました。
関数のようなマクロを書く際の問題は何ですか?
つまり、as%let myVar = %myMacro(myArgument)
を使用できるマクロ
%doSomething(withSometing)
を呼び出す%let someVar =
_ステートメントを使用してマクロ変数に値を割り当てる&myResult.
_の前の最後の行に_%mend
_と書き込みます。proc
またはdata
ステップを含めるとすぐに、これは機能しなくなりますopen
、fetch
、close
などの低レベル関数が含まれますこれをどのように解決しますか?、つまり、これを解決するためにどのビルディングブロックを使用しますか?
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;
_
私はこれが複雑であることを知っていますしかし、少なくともすべてのオタクな仕事は行われています。上司が受け入れる時間内にこれをコピー、貼り付け、変更できます。 ;
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
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_;
マクロステートメントのみを使用して記述していない限り、関数スタイルマクロから値を「返す」ことはできません。クエンティンのリンクは、これを行う方法の例を提供します。
たとえば、proc sqlは%put
ステートメントの途中で実行できないため、このようにマクロを使用することはできません(これは、dosubl
などの他のより複雑な回避策では可能ですが、それを書いた)。
%put %nobs(mylib,mydata);
大きな変更を加えずにできる最善の方法は、グローバルマクロ変数を作成し、それを後続のステートメントで使用することです。
元のマクロに対してローカルなマクロ変数を作成するには、まずマクロ定義内の%local
ステートメントを介してそれを宣言する必要があります。
私はこの議論に非常に遅れていることを知っていますが、これに遭遇したのでコメントすることを考えました。これはこれを行う別の方法だと思います:
%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);