do until(end変数)set end=を利用して、複数のデータステップを1つにまとめて共存させる方法

例えばあるデータステップを実行して、その最終結果として得られる値を、
次のデータステップで使用したい場合、常識的に考えれば当然2ステップが必要になります。

しかしdo untilループとファイル終端の際に1がたつend変数を利用することで、
複数のステップを繋いだ、かなり柔軟なデータステップを書くことができます。

今回は例として、単純なものを紹介します。

たとえば以下のように2つのデータセットがあったとします。

data Q1;
do X=1 to 5;
 output;
end;
run;








data Q2;
do Y=1 to 10;
 output;
end;
run;













そこでQ2の全オブザベーションを足して求められる値(1から10までの足し算なので55です)を
Q1の各オブザベーションにそれぞれ足したいとします。

その場合、普通はまずQ2の合計を、何らかのプロシージャやあるいはデータステップで導出し
Q1の全obsにマージしたり、或いはマクロ変数を介して足し算したりすることが浮かぶと思います。
(SQLならサブクエリで1発ですが今回はおいといてください)

ところが、なんと以下のように1ステップで書くことができます。

data A1;

 do until(eofQ2);
  set Q2 end=eofQ2;
   Y_TOTAL+Y;
 end;

 do until(eofQ1);
 set Q1 end=eofQ1;
   TOTAL=X+Y_TOTAL;
   output;
 end;

run;

で結果は








となります。
do until(eofQ2)はdo until(eofQ2=1)の略で、set Q2の際にend=でeofQ2と変数をあてているので
Q2が全て読み込まれて処理されるまで、このループが続き、抜ける頃にはY_TOTALを含む最終obsのみが
残っています。

そこからQ1のループに入りますが、今回Q1をベースとして全obs残したいのでこちらにoutputをいれています。


このように書くことで、複数のデータステップを1つにまとめることができ、
前のステップで生成された値を、リレーのバトンタッチのように使用できます。

海外のコミュニティのやりとりなどで、何回も見たことがあるので、向こうでは定跡化されている
方法なのかもしれません。

あ、ちなみに多分今回の投稿で100回目です。
あと1万倍投稿すればデータステップ100万回になるので、がんばります。

もう多分10回目の投稿ぐらいから慢性的にネタ切れな感じなので、何か意見、要望、突っ込み、アイデア、質問、なんでもください、まっています。









4 件のコメント:

  1. こんにちは。
    データステップ100回おめでとうございます!

    なんとなくデータステップじゃないみたいで面白いですね。
    POINT=オプションとかと組み合わせて複雑な処理もなんか出来そうな気がしますね、たぶん。
    海外だと色んな発想があっていいですよね。
    よくハッシュオブジェクトの話題も出てくるので、
    向こうでは、結構市民権を得てるのかもしれないですね。

    次は1000回目指してがんばってください!

    返信削除
  2. ありがとうございます!
    point=とあわせれば、確かになんか面白いことできそうですね!ループ終了条件をうまく工夫しないと無限地獄にはまっちゃいそうですが、。また今度やってみます。

    海外のプログラムのバリエーション、刺激的ですよね。
    まあでもそれ以上にmatsuさんの紹介されるコードの方が僕にはいつも
    刺激的で、勉強になってますが。



    返信削除
  3. たびたびコメントすみません。
    すでにご存じだと思いますが、point=よりlast.byと組み合わせるとかなり応用がきくんですね。
    例えば、以下みたいに検査値データなんかで、変化量【検査値 - ベースライン値(FLG="Y")】を求めたい場合とか↓

    data DT1;
    input SUBJID AVAL FLG$;
    cards;
    1 5 Y
    1 10 N
    2 4 N
    2 8 Y
    2 6 N
    ;
    run;

    data DT2;

    do until(last.SUBJID);
    set DT1;
    by SUBJID;
    if FLG="Y" then BASE=AVAL;
    end;

    do until(last.SUBJID);
    set DT1;
    by SUBJID;
    DIF = AVAL-BASE;
    output;
    end;

    run;

    またSASの世界が広がった気がします。

    返信削除
    返信
    1. うわっ何これ!すげぇ!!(笑)
      あ、でもなんか見たことある形、でもスルーしてたかも!

      一回でベースライン値とそれとの差をだせてますね。
      あぁ、これ凄いですね。そういうことか。
      外人が好んでこの形をつかったりするのはこういうのできるからか。

      やばいですね、全く意識していませんでした。
      勉強になりました。

      削除