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

SET;SET;で最大のOBSまで、足りないデータセットの最終オブザベーションを引き延ばす。

僕の中では、革命的発見です。

前の投稿で、なぜSET;SET;なのにif _N_=1で処理の終了を回避できるのかという僕の投げっぱなしの質問に対して、コメントをいただきました。
それは、SASがファイル終端を検知するのは、最終OBSを読み込んだ時点ではなく、その次を読み込もうとしてそれ以上OBSがないとなった時点ではないかと。だとするとデータセットが1obsならファイル終端が検知されるのは_N_=2で処理が行われたその時なので、それを回避すれば、処理は止まらず、値は保持されたまま引き延ばされると。

ならば、ならばですが

今、以下の3つのデータセットがあったとします。

data Q1;
do X=1 to 10;
 output;
end;
run;



data Q2;
do Y=1 to 5;
 output;
end;
run;



data Q3;
do Z=1 to 3;
 output;
end;
run;



これで、以下のコードを実行すればどうなるか

data A1;
 set Q1;
 if _N_<=tobs2 then set Q2 nobs=tobs2;
 if _N_<=tobs3 then set Q3 nobs=tobs3;
run;

以下の結果になります。


10obsに足りない部分は、一番最後の値で引き延ばされて補完されています。
これは結構使えそうじゃないですか?

コメント有難うございます。

久しぶりにとても清々しい気持ちになれました。
データステップ楽しい




1obsのデータセットの値を他のデータセットに対して引き延ばして結合する

今回は、僕が理屈を理解できていないことについて書きます。
もし説明してくださる方がいらっしゃればコメント等お願いいたします。

以下のように、10obsのデータセットと1obsのデータセットがあるとします。

data Q1;
do X=1 to 10;
 output;
end;
run;














data Q2;
 Y=5; Z=9;
 output;
run;



これに対して

data A1;
 set Q1;
 set Q2;
run;

を実行すると


結果は上記のようになります。
仮に

data A1;
 set Q2;
 set Q1;
run;

と順番を入れ替えても結果は同じです。

これについての理屈はわかります。
SET;SET;はいずれかのデータセットのファイル終端を見つけた時点でオブザベーションの
作成を打ち切るので、結果、常に指定データセットたちの中で最小のオブザベーションに
なる。ということだと思います。

しかし、

data A2;
 set Q1;
 if _N_=1 then set Q2;
run;

として実行すると結果は


となり、1obsの内容が全てに引き延ばされます。

ちなみに

data A2;
 if _N_=1 then set Q2;
  set Q1;
 run;

としても結果は同じです。

このテクニック自体は、SAS社のステートメントリファレンスでも紹介されており
以前から知っていて、何度も使ったことがあるのですが
ふと考えてみると、理屈が説明できない自分に気付きました。

やっていることはSET;SET;なわけで仮に_N_=1でしか実行されないとしても
実行された時点でQ2の1obsを読み込んだ後ファイル終点を見つけて処理が
止まって、完成は1obsになるのでは?と思ってしまいます。
PDVを理解できていない証拠なのですが、どなたか、なぜ完成が10obsになるか
を説明していただけないでしょうか?





詰めSAS8回目_循環するLAG処理(1つ前のOBSの値を取得するとともに最初のOBSについては最終OBSの値を持つようにする)

例えば

data Q1;
 do X=1 to 10;
  output;
 end;
run;


のようなデータセットがあったとして、そこから、
ひとつ前のオブザベーションの値を変数「LX」にいれる、ただし先頭については
ひとつ前のオブザベーションが存在しないので、循環して最後の値をとってくるという
問題があったとします。

目的とするデータセットは以下の形です。
↓【目的局面図】















さて、どう解くのが最短でしょうか?
ちなみに1手詰めです(1ステップで処理できる)

まず、LAG関数が第一感で思い浮かぶ方が多いかと思います。
data A;
 set Q1;
  LX=lag(x);
run;

しかし、これだと循環が表現できません。


当然こうなります。
LAG関数の使用は、今回はドツボ行きです


【解法】
data A1;
 set Q1 Q1 nobs=tobs;
  LX=X;
  if _N_<tobs/2 then delete;
 set Q1;
run;

nobs=やend=はsetで複数のデータセットを指定した際にまとめての値しかだせません。
なので上記でのnobsの中身は20です。
そこでサブセット化IFで1-9番目のオブザベーションをdeleteしているので
この時点で残っているのは10,11,12,13,14,15,16,17,18,19,20の11オブザベーション
つまり、ケツの1オブザベーションを頭に乗っけた形で11obsのデータセットが一時的に
できているわけです。
そこで、さらにSetを打つわけです。
おさらいですが、Set;Set;は一度でもファイル終端をどこかのデータセットで見つけたら
処理が打ち切られるので、結果として指定されたデータセット中、最小のオブザベーション数で
まとめられるという性質を持っています。
11obsと10obsをSet Setしているわけなので、作成されるデータセットは10obsです。

説明が下手なので、もしピンと来なければ実際に動かしてみてください。







詰めSAS2回目_複数の変数の値で転置する

SAS9.2からtransposeプロシジャのidステートメントに2個以上の変数を指定可能になりました。
あまり取り上げられるところを見たことがないのですが、これはかなり革命的なことだと思っています。いずれ取り上げたいですが、meansプロシジャのclassdataステートメントと合わせれば、医薬系で臨床試験の有害事象を群別Grade集計するのなどが、かなり楽でスマートにできるようになりました。

ほとんど答えを先に言ってしまいましたが、詰めSAS_2問目です。


data Q1;
input X Y Z;
cards;
1 4 3
2 9 8
7 6 5
;
run;







このデータセットについてX、Yの値によってZの値を転置して以下のデータセットを作ります。

目的局面図は以下です。






【解法1】
proc transpose data=Q2 out=A1(drop=_NAME_);
 var Z;
 id X Y;
run;

これだけで詰んでます。idステートメントの複数変数指定はもっと注目されるべきSASのバージョンアップポイントだと思います。

【解法2】
data A2;
 set Q1(where=(X=1 and Y=4) rename=(Z=_14));
 set Q1(where=(X=2 and Y=9) rename=(Z=_29));
 set Q1(where=(X=7 and Y=6) rename=(Z=_76));
 drop X Y;
run;

Setの連続掛けは個人的に、とてもとても好きな方法です。指定したデータセットの中で最小のobs数に統一されることと、同名の変数が上書きされる性質は扱いにくい一方で、mergeステートメントとは違った魅力があると思います。


詰めSAS1回目_全変数全obsで最大の値をとる

詰めSASの1回目のテーマは、データセット中の最大の値を1変数1obsのデータセットに格納する最善手を考えたいと思います。1手?詰めです。

まず、問いの内容は

data Q1;
input X Y Z;
cards;
1 4 3
2 9 8
7 6 5
;
run;







のデータセットから最大の値、この場合、Yの2obs目の9という値を
変数Mのみの新しいデータセットに格納するという目的です。

目的局面図は





です。


以下、解法です。

【解法1】
data A1;
 set Q1 end=eof;
 retain M;
 if max(X,Y,Z)>M then M=max(X,Y,Z);
 if eof;
 keep M;
run;

最大の値をだす場合に、第一感で思い浮かぶのはproc univariateやmeans等の
利用かもしれませんが、その場合は変数ごとの最大をだした後にデータステップで
その中からの最大をだすことになり2ステップになってしまうと思います。

なので、データステップ1回でやっつけたいのですが、SASのmax関数は横(行)方向の最大値
をとる関数なので、そこで出した値を次のobsに持ち込まないといけません。
なのでretainを使って、持ち越し、endオプションで最終obsだけ残します。

【解法2】
proc sql noprint;
 create table A2 as
  select max(M) as M
   from 
  (select X as M from Q1
    union
   select Y as M from Q1
    union
   select Z as M from Q1)
;
quit;

SQL内でmax関数は1変数内を縦にみて最大値を返すので、じゃあ全部の変数を縦に
つないでからかけてやればいいんじゃないかと思って書いてみると
思ったより頭悪い感じのコードになりました。また、サブクエリを使うと、あまり一手といえなくなって
しまいますが。これならサブクエリ内でX、Y、Zで最大をとってからCASE文で比較して最大を
とる方がスマートだったかも。

【解法3】
data A3;
 obs1=1;
 obs2=2;
 obs3=3;
  set Q1(rename=(X=X1 Y=Y1 Z=Z1)) point=obs1;
  set Q1(rename=(X=X2 Y=Y2 Z=Z2)) point=obs2;
  set Q1(rename=(X=X3 Y=Y3 Z=Z3)) point=obs3;
   M=max(of X1--Z3);
  keep M;
  output;
 stop;
 run;

これは悪ふざけです。横に最大を返すなら、全部横にしてから、かければ
それで決着という方向で書きました。

1回目はここまで。


====================================================
後日、コメントをいただいて追記
====================================================
SQLでの記述について、突っ込みをいれていただきました。
以下のコードで詰みですね!スマート!!
思いつきもしませんでした!

標準SQLのmax関数は引数が1つなのですが、複数の引数を指定した場合
SASはSQLプロシジャ内でもSASのMAX関数と認識するそうです。
奥が深い、、

proc sql;
create table A4 as
select max(max(X,Y,Z)) as M
from Q1;
quit;