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でマクロ変数を作成する方法は、まだ奥が深くて
いろいろできるのですが、今回はここまで。

7 件のコメント:

  1. こんにちは。

    順不同で記事を読んでいるので、
    ちょっと過去の記事に対するコメントですみません。

    色んな人のプログラムを見てると一般的に解法1のやり方を多く見かけますねー。
    他にobsを取得する方法として、
    ↓たとえばこんな感じで

    proc sql;
    create table A2 as
    select *
    from A;
    quit;

    %put &sqlobs;

    sqlの自動マクロ変数「&sqlobs」を使うという方法もありますね。
    「直前にcreate tableを使用している」っていう前提条件があるので、使いどころは少し限定的になりますが。。

    まぁ普通にsqlのinto使うのが一番いいですね。
    ご存知でしたら、すみません汗

    返信削除
  2. 書き込んだ後に次の記事みてみたら
    目からウロコ的なかなり効率的なやり方があったんですね。
    参考になります!

    返信削除
    返信
    1. ありがとうございます!
      やられましたよね~、1obsも読み込まずにオブザベーション数とるとは。
      SQLOBSいいですよね!忘れてました!SQLOBSのさらにいいところは処理されたOBS数が格納されるっていう点ですよね!例えばdelete文とかupdate文とかでどんなけ網にかかったかを教えてくれるみたいな。
      あ、これ次のネタにします。

      削除
  3. あ、ちなみに他の方からは、データセットが空の時に0ととりたいなら
    直前で
    %let obs1=0;
    data _NULL_;
    set A end=eof;
    if eof then call symputx('obs1',_N_);
    run;

    でいいじゃんと言われました。あぁ確かにそうだ。

    返信削除
  4. わたしは0obsになり得るデータセットのオブザベーション数を取得するときには

    proc contents data = A out = B(keep=nobs) noprint;
    run;

    data _null_;
    set B(obs=1);
    call symputx('obs1', nobs);
    run;

    という書き方をしていました。
    これだとprocステップとdataステップが必要だし、余計なデータセットを作っているのでイマイチですね。
    勉強になりました。

    返信削除
  5. 過去の記事へのコメントですみません。
    もしかしたら9.4からとかなのかもしれませんが、変数に予めセットしなくても0が入りました。

    * マクロ変数nobsに0が入るパターン;
    data test;
    aaa = .;
    delete;
    run;
    data _null_;
    call symputx("nobs", nobs);
    set test nobs=nobs;
    stop;
    run;
    %put &=nobs.;
    %symdel nobs;

    いつも勉強させていただいています!
    ありがとうございます。

    返信削除
    返信
    1. と思ったら訂正記事にかかれてましたね。
      これからも記事楽しみにしてます!

      削除