状況は限定的ではあるが、巨大なデータセットと小さいデータセットを結合する場合に、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で実行されたラインがでてくるので、それをみると何が起きたのかわかりやすいかと思います。






0 件のコメント:

コメントを投稿