ラベル call symputx の投稿を表示しています。 すべての投稿を表示
ラベル call symputx の投稿を表示しています。 すべての投稿を表示

オブザベーション数をマクロ変数に格納する方法から、SCL関数を僕が勉強していく話

以前、
SASデータセットのオブザベーション数をマクロ変数に格納する方法_call symput とsql select into: ①単純な1マクロ変数の作成


【訂正追補】SASデータセットのオブザベーション数をマクロ変数に格納する方法_call symput


でオブザベーション数をマクロ変数にいれる話をしました。

最近、リンクさせていただいたブログ「SAS Utility」の中で、全く別のアプローチでオブザベーション数を取得していて、感動しました。


記事タイトル「マクロ %nobs


SCL関数をマクロに組み込んでいるわけですね。

そういえば、同じくリンクブログの「僕の頁 <SASと臨床試験と雑談と>徒然なるままにSAS暮らし」で
fetchobs関数」の紹介をされていました

これもそっち系の技術ですね。
なんか、感覚的にfetchobs関数の処理ってハッシュオブジェクトの処理と似てません?面白いです。

実は僕はSCLの類はさっぱりで、定型文を呪文的に使うことがあるぐらいだったので、今から勉強していきたいと思います。

例えば

data Q1(label='データセットラベル');
 X=1;Y=2;Z=3;output;
 X=1;Y=2;Z=3;output;
 X=1;Y=2;Z=3;output;
 X=1;Y=2;Z=3;output;
run;









data Q2;
 A=1;B=2;output;
 A=1;B=2;output;
run;







ここで、この2つのデータセットの変数の数、オブザベーション数、データセットについているラベル
帰属するライブラリ名を取得することを考えます。




 data A1;
   DSID=open('Q1');
   VARN=attrn(dsid,"NVARS");
   OBS=attrn(dsid,"NOBS");
   DSLABEL=attrc(dsid,'LABEL');
   LIB=attrc(dsid,'LIB');
  output;
   DSID=open('Q2');
   VARN=attrn(dsid,"NVARS");
   OBS=attrn(dsid,"NOBS");
   DSLABEL=attrc(dsid,'LABEL');
   LIB=attrc(dsid,'LIB');
  output;
run;





open関数を使ってデータセット名を指定するとそのデータセットがオープン状態になって操作するための固有IDみたいなんがとれるってことですよね?そしたらそれを第一引数として、戻り値が数字になるような属性をとる場合はATTRN関数、文字の場合はATTRC関数で、第二引数に欲しいパラメータを指定するわけですね。

なるほど、なるほど、通常のデータステップで使用する場合は、ステップ終了時にオープン状態が自動解除されるので、特に気にしなくていいけど、マクロ内で%sysfuncで実行する場合は、オープンのままになってしまうから、close関数を使うわけですね。

ちょっとずつわかってきました。





SYMGET関数でマクロ変数の値を取得する

マクロがあまり得意ではないので、もし誤っている点があればご指摘ください。



%let MA=11;
%let MB=22;
%let MC=33;
%let X=44;

のようにマクロ変数に値が代入されているとします。

そして以下のようなデータセットがあったとします。

data Q1;
X='MA';output;
X='MB';output;
X='MC';output;
run;







そこでXの値に対応するマクロ変数の値を展開したい場合、次のように書けます。

data A1;
 set Q1;
  Y=symget(X);
run;








あるいは

data A1_;
 set Q1;
  Y=resolve('&'||X);
run;

でも可です。resolve関数はより広義的機能をもってたりするので興味のある方は
リファレンスやヘルプをひいてください。

でsymgetの戻りはlength$200がデフォで、つまり文字型ですが

data A2;
 set Q1;
  Y=symgetn(X);
run;

とsymgetn関数にすれば数値で得られます。


また

data A3;
 set Q1;
  Y=symget('X');
run;

とすると







単純にマクロ変数Xを展開するので、上記のようになります。


もう一点ポイントなのですが、

以下のプログラムをみてください

data A4;
 set Q1 end=eof;
 call symputx(cats('MV_',_N_),X);
 if eof then Y=cats("&MV_1","&MV_2","&MV_3");
run;









このプログラムでは、最後のY=のところでマクロ変数を展開することができません。
call symputはデータステップ中にマクロ変数に値を代入できますが、
それはそのデータステップが終了した時点で確定し、普通の方法では同データステップ内で
参照することはできません。

%symdel MV_1 MV_2 MV_3 MA MB MC X;
(※順に実行して確かめている方のために一度マクロ変数を消しています)

ところが

data A5;
 set Q1 end=eof;
 call symputx(cats('MV_',_N_),X);
 if eof then Y=cats(symget("MV_1"),symget("MV_2"),symget("MV_3"));
run;







のように書けば、symputで作成されたマクロ変数を同ステップで参照できます。



【訂正追補】SASデータセットのオブザベーション数をマクロ変数に格納する方法_call symput

前回の投稿で、データセットのobs数をマクロ変数に格納する際に

data _NULL_;
 set A end=eof;
  if eof then call symputx('obs1',_N_);
run;

を提示し、問題点として、データセットが空であった場合、マクロ変数が作成されず
0がとれないと挙げたのですが、

data _NULL_;
 call symputx('obs',tobs);
 if 0 then set A nobs=tobs;
 stop;

run;

このようにすればいいのではないかとご指摘をいただきました。
その通りでした。素晴らしいです。

この場合、1行も読み込むことなくマクロ変数を作成しているので
元データセットが巨大で、obs数をとりたいだけの場合などに
余計な読み込みで処理時間を無駄にすることもなく最強です。
data _NULL_で_N_使うのは確かに無駄でした。反省します。

nobs=で任意の変数にデータセットのobs数を格納することができるのですが
どうもこれ、データステップの開始の時点で格納されるらしく、setステートメントの前、
というかコードの先頭ですでに取得可能なんですね、面妖な、、。

if 0 はif _N_=0 などでもよく、ようは絶対に実行されないif文ですね。
絶対に実行されないif文は、実行はさせないけど、データの定義情報(ディスクリプタ部)を
つかいたい時の方法ですね。知らないと、何してんだこいつ的コードですが。

Sさん有難うございます。
でも会社で、おっきな声でブログが、って言わないでください、、、。



SASデータセットのオブザベーション数をマクロ変数に格納する方法_call symput とsql select into: ①単純な1マクロ変数の作成

SASデータセットのオブザベーション数をマクロ変数で取得したいことはよくあります。

data A;
X=1;output;
X=2;output;
run;

という2オブザベーションのデータセットがあったとします。

【解法1】

data _NULL_;
 set A end=eof;
  if eof then call symputx('obs1',_N_);
run;

call symputx ルーチンでSASの自動変数_N_をobs1というマクロ変数に入れています。
_N_は数値型ですがsymputxは余計な空白を含まない文字型に変換してから
格納してくれるのでログに型変換のNoteも出力されません。

end=をつけてifステートメントをかけているのは
これをつけないと、1オブザベーション読み込むたびにcall symputx ルーチンで
マクロ変数を更新してしまうからです。欲しいのは最後のオブザベーションの_N_だけなので
余計な関数処理にかかる時間を短縮するためこのように書いています。

さて、このcall symputxを利用する方法の問題点は、オブザベーション数が0、つまり空であった場合にマクロ変数が作成されないという点です。
後のコードで、作成されたマクロ変数を参照している場合、条件分岐していないと、マクロ変数が存在しないためエラーになる恐れがあります。

また下のように、サブセット化IFステートメントとcall symputルーチンの併用も危険です。

data _NULL_;
 set A end=eof;
  if X<=1;
  if eof then call symputx('obs1',_N_);

run;


【解法2】

proc sql noprint;
 select count(*) into:obs2
  from A;
quit;

SQLの場合、count関数はデータセットが空の場合も0を返してくるので
マクロ変数は必ず作成されます。
select ○○ into : マクロ変数名で、マクロ変数に値を代入します。
ただし、クエリが返すオブザベーション数には注意してください。

また、条件を加える場合も

proc sql noprint;
 select count(*) into:obs2
  from A
  where X<=1;
quit;

こんな感じで、自然にかけば、それが正解です。

SQLでマクロ変数を作成する方法は、まだ奥が深くて
いろいろできるのですが、今回はここまで。