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

更新回数をカウントしながらデータをupdateする機能を、ハッシュオブジェクトで実現する話

UPDATEステートメントはかなり応用の効くステートメントです。
UPDATEステートメントの基本についてはSAS忘備録の
「UPDATEステートメント入門」
http://sas-boubi.blogspot.jp/2014/06/update.html
が素晴らしいので、まず読んでください。

その上で、

マスターとなるデータセット

data MAS;
 X=1;Y='あ';output;
 X=2;Y='ろ';output;
 X=3;Y='ほ';output;
 X=4;Y='に';output;
run;









トランザクションデータセット

data TRA;
 X=1;Y='ほ';output;
 X=1;Y='ろ';output;
 X=1;Y='い';output;
 X=2;Y='ろ';output;
 X=3;Y='ひ';output;
 X=3;Y='は';output;
 X=5;Y='と';output;
 X=5;Y='ほ';output;
run;












を用意して、

data A0;
update MAS TRA;
by X;
run;

とすると結果は









となります。

トランザクションデータセットに同一キーのものが複数存在する場合、
結果は最終的に出現したデータになります。

例えばX=1でいうと、Yは「ほ」、「ろ」、「い」という順にトランザクションから読み込まれるので、
最終的にマスターデータセットのX=1に対応するYの値は「い」です。

ただ、これを、「ほ」で更新され、「ろ」で更新され、最後に「い」で更新され、
結局値が確定するまで3回上書き更新されたと捉え、この3回というカウントを変数として持たせたいとします。

昔、仕事で、最終的な更新結果を出すと同時に各オブザベーションが何回更新されたか。
つまりトランザクションデータに同一キーが結局いくつ出現したかを出さなければいけない状況がありました。

その時はまだ初心者でハッシュオブジェクト??だったので、普通にupdateしたあとで、別ステップでトランザクションデータのキーをfreqかなんかで集計したデータセットを作って、update後のデータセットとマージして切り抜けたと思いますが。

ひねくれた現在の僕なら以下のようにかけます。

あ、ちなみに今回は、問題を単純化にするためにトランザクションデータにnullは含まれないという前提条件をつけましょう。
updateステートメントにはnullの場合、更新を行わないという機能がありますが、ハッシュオブジェクトでやる場合、それで一つ条件分岐を増やさなければいけないので。
あとマスターの値とトラの値が同一であったとしても、同値で更新を行ったと考えます。

data _NULL_;
  if _n_ = 1 then do;
   declare hash hx(suminc:'UP_COUNT', ordered: 'yes');
    hx.defineKey('X');
    hx.definedata('X','Y','UP_COUNT');
    hx.definedone();
     end;
 do while (^FL);
   set MAS end=FL;
   UP_COUNT=0;
   hx.add();
  end;

 set TRA end=eof;
 UP_COUNT=1;
 rc=hx.check();
 if rc=0 then do;
  hx.sum(sum:up_count);
  hx.replace();
 end;
 if rc^=0 then do;
  UP_COUNT=0;
  hx.add();
 end;
 if eof then hx.output(dataset:'A1');
run;

で、結果は









です。

マスターに存在しなかった値が追加された時にカウントを1とするかどうかについて、今回は、追加は更新カウントに含めないとしています。

以前、紹介したsumincを使用して、checkメソッドによるキー参照をカウントしています。
checkメソッドにより、キーがハッシュオブジェクトに見つかればreplaceで更新、ないならaddメソッドで追加という、極めて単純な構造です。

このコードは、思いつきで、えいやっと書いたやつなんですが、なかなか美しくないでしょうか?
反復子オブジェクトを使わずに表現できていますし、自分的に結構好きですね。


UPDATEステートメントを使ってみる

SASで結合といえば、なんだかんだいってやっぱりMergeステートメントが幅を利かせています。
書きようによって、多対多のようなケースを除いて、ほぼ全ての結合を表現できます。
Merge最高!Merge万歳!
が、場合によっては、UPDATEステートメントを使用した方が見通しがよい場合もあるので紹介します。
ちなみに僕はMergeステートメントあまり好きでありません

今データセット「MAS」と「TRA」があったとします。
UPDATEステートメントはマスタ―データセットとトランスザクションデータセットという考え方を持ち、
マスターをトランザクションで更新するといった機能になります。

data MAS;
 X=1;Y='あ';output;
 X=2;Y='ろ';output;
 X=3;Y='';output;
 X=4;Y='に';output;
run;

【MAS】


data TRA;
 X=1;Y='い';output;
 X=2;Y='';output;
 X=3;Y='は';output;
 X=5;Y='ほ';output;
run;

【TRA】







とりあえず X でソートします。
proc sort data=MAS;
 by X;
run;
proc sort data=TRA;
 by X;
run;



上の二つから以下の結果が欲しいとします。








変数Xをキーにして単純にMergeで結合するとTRAの変数Yがnullのため
結果のX=2に対応するYはnullになります。TRAにwhereでY^=''をつければいいのですが
そんなひと手間かけなくても

data UP;
 update MAS
        TRA;
     by X;
run;

で詰みです。
UPDATEステートメントはトランザクションデータセットにおいて値がnullの場合は
マスターデータセットの該当変数を上書きしないという付加機能を有するわけです。

ちなみに

data UP2;
 update MAS
        TRA updatemode=nomissingcheck;
     by X;
run;

のようにupdatemode=nomissingcheckとすると欠損値でも更新するので
すなわち

data MG;
 merge MAS
        TRA;
     by X;
run;

と同じになります。

ちなみにUPDATEと同様の表現が可能なMODIFYステートメントがいるのですが、
こいつは多機能で、深いので、いつか勉強してから紹介します。
LIBNAME EXCELで値とエクセルにだす時以外、あんまり使ったことないので、、

ちなみに今回のケース、SQLなら

proc sql noprint;
 create table UP3 as
  select coalesce(TRA.X,MAS.X) as X
              ,coalesce(TRA.Y,MAS.Y) as Y
  from MAS full outer join TRA 
           on MAS.X=TRA.X;
quit;


こんな感じでしょうか?
僕の好きなcoalesce関数です。coalesceの順番がトランザクション→マスターなところが味噌ですか。
あとfull outer joinは両側外部結合です。どっちかにでもあればとってくるよというやつです