転置して連結してマージみたいな処理を一切ソートせずに行ってみる

以前の記事、「大半のソートは百害あって一利無しという話」
http://sas-tumesas.blogspot.jp/2016/04/blog-post.html

が結構反響あって、最近よくハッシュオブジェクトについて質問されることが多いです。

昔はよく、職場で提案したら「導入のメリットがわからない」「コードが難しそうだから」とかで一蹴されちゃいました!とかってネガティブな報告をもらってました。

近頃では、やっと導入するようになりました!という話や、単純な使用法から一歩踏み込んで、multidataやハッシュ反復子オブジェクト関連の質問までもくるので、日本でもようやく普及してきたのかなぁと嬉しく思います。次の僕の興味はDS2に傾いちゃってますが…。

ともあれ、まだハッシュオブジェクト使っていない方に、興味を持ってもらうには、具体的な使用例をできるだけ多く上げていくのがいいのかなと思ったので、最近質問があってハッシュを使った例を紹介してみます

以下の2つのデータセットがあったとします

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











data Q2;
x=1;y=1;output;
x=1;y=2;output;
x=1;y=3;output;
x=3;y=4;output;
x=3;y=5;output;
x=4;y=6;output;
run ;













Q1に、xをキーにしてQ2のyをzっていう名前で取得する、複数ある場合は「,」で連結してねっていう処理を考えます。
出力したいのは以下の形です











多分、以下のような流れで書く方が多いんではないでしょうか

proc sort data=Q1;
by x;
run;
proc sort data=Q2;
by x;
run;
proc transpose data=Q2 out=_Q2 prefix=y_;
var y;
by x;
run;
options missing='';
data A0;
merge Q1(in=in1)
 _Q2;
by x;
if in1;
z=catx(',',of y_:);
keep x z;
run;
options missing='.';

上記の場合、4ステップで、sortが2回、transposeが1回入っています。

これをハッシュオブジェクトを使うと1ステップで記述できます

data A1 ;
length x 8. z $200.;
 if _n_ = 1 then do ;
 if 0 then set Q2 ;
 dcl hash h1 (dataset: "Q2", multidata: "y") ;
 h1.definekey ("x") ;
 h1.definedata ("y") ;
 h1.definedone () ;
 end ;
 set Q1 ;
 do rc = h1.find() by 0 while (rc = 0) ;
  z=CATX(',',z,y);
  rc= h1.find_next() ;
 end ;
keep x z;
run ;

結果は同じです。

ハッシュオブジェクトのmultidataは昔に一度紹介したことがありますがそれと同じですね。

「ハッシュオブジェクトの世界⑧ keyの重複を許容する multidata find_nextメソッド」


 do rc = h1.find() by 0 while (rc = 0) ;の部分は
ちょっと小洒落た、というかスカした書き方ですね。意味がわからない場合は過去記事のように
単純にわけて書いてOKです

ちょっと説明すると、ようはfindメソッドと、fined_nextメソッドの戻り値判定を一つにまとめてるんですね。
こういう風にifとかdoにメソッドの戻り値をもってくる書き方してもメソッドは実行されるんですね。
by 0にしてるのは、これをつけないとデフォルトで1ずつ増えてしまうからです。
rc=0、つまりメソッドがキーを見つけれている間はずっとのループを表現してるんですね。

前準備のためのソートや転置が一切不要になっているので、コードとしては効率化できたといえると思います。

例では文字列連結しましたが
catxの部分の処理を変えれば、例えばyの合計値や平均値をマージするプログラムも簡単にかけるわけですね。(DS2ならもっと簡単で、sqlで計算してからdataset指定できますけど)

また何かいい例があれば紹介したいと思います


0 件のコメント:

コメントを投稿