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

do to by ループのtoを消してもループは成立するので、それを使って1手得を狙っていく話

以前、「do until(end変数)set end=を利用して、複数のデータステップを1つにまとめて共存させる方法」
http://sas-tumesas.blogspot.jp/2013/12/do-untilendset-end1.html

で、do loopの中にsetコマンドを突っ込むやりかたを紹介しました。

また、その方法については「SASインタリーブ(interleave)の紹介と再帰的インタリーブで、SQL再マージ処理をデータステップで疑似的に表現する」
http://sas-tumesas.blogspot.jp/2014/01/sasinterleavesql.html

でも少しふれましたし

最近、「詰めSAS10回目_変数内の1文字ごとにオブザベーションを起こす」でa matsuさんが
提案してくれた方法もdo ループとsetをつかっています。
http://sas-tumesas.blogspot.jp/2014/01/sas101.html


で、これとほぼ同じ内容にはなるのですが
ループの中でカウンタとする変数を増加する場合、
ループの中で変数+1と割り当てる方法があるのですが、最近、それを省略してもかけるんじゃないかと思いつきました。

ちょっと細か過ぎて、この話題で喜ぶ人がどれくらいいるか謎ですが、

たとえば

data Q1;
X='A';output;
X='A';output;
X='Z';output;
X='B';output;
X='Z';output;
X='A';output;
X='B';output;
X='C';output;
X='Z';output;
run;













こういうデータがあって、
Xの値に「Z」が出現するまでのオブザベーション数をカウントし、出現した行の番号と
ともに出力するというプログラムをかくとします。

求めたい結果は







元データの3,5,9行目でZが出現しているのでROWには3,5,9が入り、
そこまでに出現するZ以外の文字のカウントがCOUNTに入ります。


さて、どう切り込みますか?

今回の場合、元データの格納順を崩せないので
ソートを利用したfirst last系の分岐を利用できないんですね。

多分、第一感はretainだと思います。

data A0;
 set Q1;
 retain COUNT 0;
 COUNT+1;
 if X='Z' then do;
  ROW=_N_;
  output;
  COUNT=0;
 end;
drop X;
run;

で詰みですね。

これでも全然いいんですが、do loopを使うなら

data A1;
 do COUNT=0 by 1;
  set Q2;
  ROW=monotonic();
  if X='Z' then leave;
 end;
 drop X;
run;


これでも詰みなんです。

do loopの to条件を省くことでループ変数そのものをカウンタ変数として使っちゃうわけですね。
setがループされるため、ここで_N_つかっても1,2,3とはいるだけで元データの行番号をとるうえではポンコツなのでmonotonic関数でとります。
ちなみにここでのleaveはoutputでもいいです。leaveでもオブザベーション起きるのが面白いので
leaveにしてます。

ちなみにifで抜けなくても、until終了条件でも当然OK

data A2;
 do COUNT=0 by 1 until(X='Z');
  set Q2 end=eof;
  ROW=monotonic();
 end;
 drop X;
run;




SQLプロシジャで_N_やfirst. last.の処理を疑似的に表現する_列番号を返すmonotonicを利用して

SQLで_N_やfirst. last.を使えますか?と聞かれることがあります。

SQLは本来、データ格納の位置やソート順に縛られない言語なので
そういうのはデータステップでやってくださいなというところです。

実際、標準SQLには列番号を取得するような関数は意図的に存在しないのですが
実はSASのSQLプロシジャには、monotonic関数という独自関数があります。
こいつを使うことで、_N_やfirst. last.と結果が同じになる処理を行うことはできます。

本来のSQLの理念に反しているということで、賛否両論、批判的な人も多いですが
まあ、実際あるもんだから、知ってて損はないはずです。

以下のようなデータセットがあったとします。

data Q1;
 do X=1 to 5;
  do Y=1 to 3;
   output;
  end;
 end;
run;



















if _N_=3で絞ってデータセットを作る処理、つまり3番目に格納されているobsのみ残すのを
をSQLプロシジャでやるなら

proc sql noprint;
 create table A1 as
  select X,Y
  from Q1
  where monotonic()=3;
quit;





となります。
monotonic()がいわゆる行番号を返します。

つまり

proc sql noprint;
 create table A2 as
  select X,Y,monotonic() as N
  from Q1;
quit;

の結果は



















となります。

で、ここからは応用でfirst.Xまたはlast.Xのみを残す処理をかいてみます。
多分、すでに予想がついているかと思いますが、案の定group byとhaving及び、minまたはmax関数を使います

first.Xは

proc sql noprint;
 create table A3(drop=N) as
  select X,Y,monotonic() as N
  from Q1
  group by X
  having min(N)=N
;
quit;

で結果は









となりlast.Xは

proc sql noprint;
 create table A4(drop=N) as
  select X,Y,monotonic() as N
  from Q1
  group by X
  having max(N)=N
;
quit;









となります。

まあ、あくまで結果を同じにしているだけで、データステップと同じように動いているわけではないので注意。

【追記】
う~ん、matsu a  さんもリンク先で指摘されているので、あんまり頼らない方がいいかもしれませんね。。
http://sas-boubi.blogspot.jp/2014/02/sqlmonotonic.html