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

状況は限定的ではあるが、巨大なデータセットと小さいデータセットを結合する場合に、call executeとproc appendを利用した極めて効率的な方法

巨大なデータセットと小さいデータセットの情報を両方使って処理をする。
処理効率をよくするにはどうするか(実行時間の短縮)??

典型的な例なのでハッシュオブジェクトの利用を思いつく方が多いかもしれません。
(私事ですがハッシュオブジェクトについては現在研究中で、 来年のユーザー総会の発表テーマにしたいと思っているので取り上げる回数が少ないかもしれませんが、軽んじているわけではないです。)

しかし、内部結合した結果のみ残すという条件と、小さい方にあるキー値がかならず大きい方に含まれているという限定的な条件が加わった場合(両方にキーが存在するobsのみ残す)においてのみ、劇的に処理効率のよい方法があります。

論文[Merging Data Eight Different Ways]や[Countdown of the Top 10 Ways to Merge Data](著者は同じ)でも紹介されている、call executeとproc appendを使った離れ業です。
論文中のDATA=dat0の部分はDATA=alldatの誤記か誤植かと思いますが、それにしても
ぶっとんだ発想なので、まねてみます。

data Q1;
 do X=1 to 1000000;
   Y=X*2;
  output;
 end;
run;




















(画像は途中まで)

という100万オブザベーションのデータセット(欲をいうと変数Xにインデックスがついてると最上ですが)と

data Q2;
 X=123;Z='い';output;
 X=333;Z='ろ';output;
 X=423;Z='は';output;
run;







という3obsという小さいデータセット(今回の方法では小さいほうが、obs数が少なければ少ない程効率がよい)があって

変数Xを使って結合するとします。
求める結果は







こんな感じです。

考え方として、100万の方に対して、3obsしか読みこみたくないわけですね。100万全部読み込んでから3obsを残す処理にすると、どうしても読み込み時間がかかるのでそれを回避したいのです。
じゃあ、whereを使って、抽出したいわけ(サブセットIfと違いwhereは読み込む前に抽出する)ですが、その条件とQ2、結合したいデータもQ2に入っちゃているわけです。

じゃあどうすんの、で、以下の回答です。

DATA _NULL_;
 set Q2;
 call execute("data Q3;
                     set Q1;
                     where X="||X||";
                      Z='"||Z||"';
                    run;
                  proc append base=A1 data=Q3 force;
                  run;");
run;


小さい方のQ2をセットしてデータステップを開始して1obs読み込むごとに読み込んだXの値で
でかい方のQ1にwhereで抽出をかけて、そこに読み込んだZを加えた上で一時的にQ3という
データセットを作り、proc appendで本来作成したい最終結果となるA1にQ3を追加しているのですね。call executeの部分が疑似的なループ処理になるので、回数が少ないほど、つまり、Q2のobs数が少ないほど良いといったのはそういう理由です。

多分コードだけでイメージをつかみにくいと思うので、実際に実行してみてください。
ログにcall executeで実行されたラインがでてくるので、それをみると何が起きたのかわかりやすいかと思います。






CALL EXECUTEで、マクロループみたいなことを平コードで実現する_条件に合致するデータセットに対して処理を掛ける例

CALL EXECUTEというと、データステップ内で発生した値をマクロの引数にして実行する時によく使う機能でマクロとセットで捉われがちですが、
本来、CALL EXECUTEの引数を必ずマクロ化した処理にしなければならないわけではありません。


例えば今、以下のようにDS1,DS2,DS3,DS4と適当なデータセットが4つあったとします。

data DS1;
 do X=1 to 10;
  output;
 end;
run;
data DS2;
 do X=10 to 20;
  output;
 end;
run;
data DS3;
 do X=20 to 30;
  output;
 end;
run;
data DS4;
 do X=30 to 40;
  output;
 end;
run;

そして、以下のLISTというデータセットがあったとします

data LIST;
 DSNAME='DS1';FLAG='Y';output;
 DSNAME='DS2';FLAG='N';output;
 DSNAME='DS3';FLAG='N';output;
 DSNAME='DS4';FLAG='Y';output;
run;







FLAG変数がYとなっているDSNAMEのデータセットに対して
要約統計量の入ったデータセットを作成したいとします。

その場合、以下のようにLISTをデータステップでsetし
ifステートメント後にcall executeで、実行したい処理を文字列として記述します。
処理の引数にしたい部分は、データステップの変数なのでコーテーションでくるまないように
気をつけます。

data _NULL_;
 set LIST;
 if FLAG='Y' then
 call execute("proc summary data="||DSNAME||";
               var X;
               output out="||DSNAME||"_RESULT;
               run;");
run;

これで実行するとDS1とDS4に対してのみproc summaryが実行され
DS1_RESULTとDS4_RESULTが作成されます。





















もし、マクロ化してからcall executeするのであれば以下の感じでしょうか。

%macro m_d(ds);
  proc summary data=&ds.;
   var X;
   output out=&ds._RESULT;
  run;
 %mend m_d;

 data _NULL_;
  set LIST;
  if FLAG='Y' then
  call execute('%m_d('||DSNAME||')');
 run;