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

同IDグループの中で、n個前のオブザベーションを参照したり、n個先のオブザベーションを参照したりする処理がハッシュオブジェクトなら割と簡単にできる話

※ちょっとコード修正しました。1obsごとにハッシュオブジェクト定義するようになっていて、効率悪い書き方してました。

SASで、1つ前のオブザベーションが変数の値が欲しい!とか、1つ先が欲しいとかってよくある話です。n個前のということであれば、retainステートメントや、或いはlag関数などで実現できます。

また、n個先であれば、例えばSAS公式の
「次オブザベーションの値を参照する方法」
http://www.sas.com/offices/asiapacific/japan/service/technical/faq/list/body/ba212.html

や、他にpoint=オプションを使った方法(いずれ紹介するかも)などが利用できます。

ただ、例えば、同じIDグループの中で次のオブザベーションといった制限がつくと、少し面倒になります。
(retainであればbyの部分と合わせて、first.で値をリセットしていけばいいし、先をとるためのmergeなら、予めマージキーとしてID内連番を作成して、それに細工をすればできます)

しかし、扱うデータセットのサイズが何万obsとかの巨大なものでないのであれば、ハッシュオブジェクトを使うとかなり楽にでき、さらにより複雑な条件であっても容易に組み込むことができるので、個人的にはお勧めです。

ただし、できれば、ハッシュオブジェクトを勉強して、理屈をある程度理解した上で利用するようにした方がいいと思います。
何をしているのか全くわからずに、コピペからカスタマイズすると、少し危険に思います。

過去のハッシュオブジェクト関連の記事
http://sas-tumesas.blogspot.jp/search/label/ハッシュオブジェクト


それではやってみましょう。

data Q1;
ID='001';X=1;output;
ID='001';X=3;output;
ID='001';X=5;output;
ID='001';X=7;output;
ID='001';X=9;output;
ID='001';X=11;output;
ID='002';X=2;output;
ID='002';X=4;output;
ID='002';X=6;output;
ID='002';X=8;output;
run;


















というデータがあって、同じIDの中で、1obs前のXの値をX_m1に、1obs先のXを_p1、2つ先をX_p2という変数にいれるとします。

data A1;
 if _n_ = 1 then do;
   declare hash hx();
    hx.defineKey('ID','NO');
    hx.definedata('_X');
    hx.definedone();
  do while (^FL);
   set Q1 end=FL;
   by ID;
   if first.ID then NO=0;
   NO+1;
   _X=X;
   hx.add();
  end;
 end;
   set Q1;
   by ID;
   if first.ID then RNO=0;
   RNO+1;
   
   /*同ID内の一つ前のデータを参照*/
   NO=RNO-1;
   rc=hx.find();
   X_m1=ifn(rc=0,_X,.);

   /*同ID内の一つ先のデータを参照*/
   NO=RNO+1;
   rc=hx.find();
   X_p1=ifn(rc=0,_X,.);

   /*同ID内の二つ先のデータを参照*/
   NO=RNO+2;
   rc=hx.find();
   X_p2=ifn(rc=0,_X,.);

 keep ID X X_m1 X_p1 X_p2;
run;


結果は


















このように、ハッシュオブジェクトは、データステップ中の処理と独立したルックアップテーブルを作成して、データステップ中に値のやりくりができるので、使いこなせば、プログラミングの自由度がかなりアップします。