詰めSAS4回目_直積(デカルト積、単純結合)を作成する

直積(デカルト積、単純結合)はデータセット同士の全オブザベーションの組み合わせのようなもので3obsのデータセットと4obsのデータセットの直積をとると12obsになります。
実践で使用する機会がそれほど多いとは思いませんが、たまにあったり、また直積を作った方がスムーズに以後の処理ができることもあります。

問題は
data Q1;
input A B;
cards;
1 2
3 4
5 6
;
run;
【データセットQ1】




data Q2;
input C $ D $;
cards;
A B
C D
E F
;
run;
【データセットQ2】






の二つから











のようなデータセットを作ることです。





【解法1】
data A1;
set Q1;
 do i=1 to Q2OBS;
  set Q2 nobs=Q2OBS point=i;
  output;
 end;
run;

まずはデータステップで詰みです。nobs=オプションはデータセットの総obs数をとれるのですが
それをsetステートメントの前に使用できるというのは意外です。それをループ終点にして
point=でダイレクトアクセスする度に明示的にOutputすることで、Q1のSetによって、Q1が1obs読まれる度にi=1からQ2の最後のobsまでOutputされ、それがQ1のSetが全obs完了するまで行われるので結果として直積が作成されます。2つのデータセットのサイズによって変わりますが
基本的にはこれが最も効率の良い作成法だと思われます。コードもコンパクトにまとまってます。


【解法2】
proc sql noprint;
 create table A2 as
  select *
  from Q1,Q2;
quit;

直積はSQLの基本ともいえるので(単純結合というぐらいですし)、SQLで詰ませます。
cross join等の方が綺麗な書き方なのかもしれませんが、とりあえず一番単純な書き方で実現です。SQLを使う場合の注意点は、ログにnoteとしてデカルト積が生成されたことを知らせるメッセージが必ずでるので、ログの内容に厳格に注意を払う仕事では留意する必要があります。

【解法3】
data A3;
 if _N_=0 then do;
  set Q1 Q2;
 end;
 if _N_=1 then do;
  declare hash hq(dataset:'Q2');
  declare hiter hi('hq');
   hq.definekey('C','D');
   hq.definedata('C','D');
   hq.definedone();
 end;
  set Q1;
  rc=hi.first();
   if rc=0 then output;
  do while(rc=0);
   rc=hi.next();
   if rc=0 then output;
  end;
 drop rc;
run;

ハッシュ反復子でやってみました。ハッシュはメモリにデータを展開するので、作成が高速化するか
と思ってやったのですが、コードが悪いのか、いくつかテストした条件ではデータステップやSQLの
1.5~2倍遅い結果しかだせていません。効率悪いはコードは長くて意味不明だわでいいとこなしな
感じですが、恐らくQ1がかなり巨大でQ2もそこそこの大きさのデータセットにして、オプションで
ハッシュの割当サイズを適度にすれば、速くなったりする気がします。いや、そもそも直積にハッシュはあまり意味がないのか。すみません、勉強中です。



0 件のコメント:

コメントを投稿