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

DO LOOPとcontinueステートメントで逆に考えるんだ 

例えば1から10までの数字で、奇数のものだけをオブザベーションにもったデータセット









を作れと言われたら、第一感で浮かぶのは

data A1;
do X=1 to 10;
  if mod(X,2)=1 then output;
end;
run;


こんな感じのコードではないでしょうか?
ループをまわして、条件に合った時のみ処理するというのは、自然な発想だと思います。


でも敢えて逆に考えてみます。

余りが1の時に処理するのではなく、余りが0の時に処理しないというように考えてみます。

すると

data A2;
do X=1 to 10;
  if mod(X,2)=0 then continue;
  output;
end;
run;

こうなります。

結果は同じです。

この発想の方が、プログラムが自然にかける時もあるので、覚えておいて損はないはずです。


ちなみに、continueとleaveは同違うのかというと、continueがスルーしてループを継続するのに対して、leaveはその時点で、ループを打ち切ります。

なので

data A3;
do X=1 to 10;
  if mod(X,2)=0 then leave;
  output;
end;
run;

すると





ってなっちゃいます。


do i=1 to ○○ until();のようにdo toループとuntilやwhileが併用できることを今まで知らなくてとても悲しかった話

とてもショックです。

data A1;
X=20;
 do i=1 to 99 until (X=40);
  X+2;
 output;
end;
run;

99回ループする。ただし途中終了条件としてX=40になれば終了する。
この処理を上記のコードでかけるのです。
こんな単純な書き方なのに気づきませんでした。ショックです。悲しいです。

以前は以下のように書いていました。

data A2;
X=20;
 do until(i=99 or X=40);
  X+2;
  i+1;
 output;
end;
run;

最近、leaveステートメントを知って、その時もショックでしたが、
以来、以下のように書けるようになりました。

data A3;
X=20;
 do i=1 to 99;
  X+2;
  output;
  if X=40 then leave;
end;
run;

SASのデータステップの知識の全てを100としたら、多分そのうち理解できている部分は
1以下だろうと思い知りました。

性根を叩き直して勉強しなおします






配列名に対してinで抽出をかける

以下のようなデータセットがあったとします。
(作成法は適当です。
多分、0.4の確率で'A',0.5で'B',0.1で'C'となる3変数で構成されるデータセットになっているはずです)

data Q1;
call streaminit(12345);
 do i=1 to 15;
  X_1=strip(translate(put(rand('table',0.4,0.5,0.1),best.),'ABC','123'));
  X_2=strip(translate(put(rand('table',0.4,0.5,0.1),best.),'ABC','123'));
  X_3=strip(translate(put(rand('table',0.4,0.5,0.1),best.),'ABC','123'));
  drop i;
 output;
end;
run;





X_1 X_2 X_3を配列AXとする時に、配列AX内のいずれかの要素が値'C'をとる
オブザベーションのみ出力せよ。
といった問題があるとします。

欲しいのは以下のデータセットです




最近まで、本当に知らなかったんですが

data A;
 set Q1;
 array AX{*} X_:;
   if 'C' in AX then output;
run;

これで詰みなんですね。値 in 配列名 で通るなんて、びっくりですよ。

基本的な知識だと言われたのですが、こんなんどこで習うの?知らなかったの僕だけですか?
どうぜ、そういうことって、他にもいっぱいあって、

知ってれば簡単に済む処理を、日々、無駄なコードをこねくりまわして
作っているだろうな、僕は! と切なくなりました。

てっきり

data A0;
set Q1;
 array AX{*} X_:;
  if count(cats(of AX{*}),'C')>0;
run;

とか

data A1;
 set Q1;
  array AX{*} X_:;
  do i=1 to dim(AX);
  if AX{i}='C' then do;
    output;
    leave;
   end;
  end;
 drop i;
run;

が正解かと思ってましたよ。











詰めSAS3回目_最初にnullでない変数の値を取得する

変数を順番にみていって、null以外が初めて出現する際の値を取得する。また全ての変数がnullである場合、特定の値を代入する。
詰めSAS3回目は少し変わった処理になります。

問題は以下のデータセット

data Q3;
length A B C D $2.;
A='';B='ろ';C='';D='に';output;
A='い';B='ろ';C='は';D='に';output;
A='';B='';C='は';D='に';output;
A='';B='';C='';D='に';output;
A='';B='';C='';D='';output;
run;









について、A→B→C→Dとみていき、nullでない最初の値、すべてnullの場合は「へ」と返し
目的局面図









のようなデータセットを作成します。



【解法1】
data A1;
length E $2.;
 set Q3;
  E=coalescec(A,B,C,D,'へ');
 keep E;
run;

第一感として、配列とdo loopで処理する方法があると思うのですが
最短最善はcoalesce関数(今回は対象が文字変数なのでcoalescec)だと思っています。
引数のうち、指定順にnullでない最初のものを返します。
もともとSQLで使われていた関数のSASへの輸入だと思いますが、こいつは非常に優秀な
関数で、横方向への順次走査のような処理を関数でできてしまいます。定数をつかえば
最終的にnullの場合に定数を与えることができます。
たとえば臨床試験で、CRFにチェックボックスがいくつかあるもので、
よく、一つでもチェックがあれば何かのフラグをたてるといった処理がありますが
これをつかえば、ifステートメントを書き連ねる必要がなくなります。
ちなみにcoalesceはコアレスと発音します。
僕はコールエッセ関数とずっと呼んでいて恥をかきました。コアレスなんて読めます?

【解法2】
data A2;
 set Q3;
  array AR{4} $ A B C D;
   do i=1 to 4;
    if AR{i}^='' then do;
    E=AR{i};
  leave;
   end;
  end;
  if E='' then E='へ';
 keep E;
run;

配列ならこんな感じでしょうか?実は最近までleaveステートメントの存在を知りませんでした。
配列を抜けるときに使うのですね。