2023年5月7日日曜日

「PROC REPORT」の「COMPUTE」がうまく動かないんだけど②



以下記事の続き。



失敗例1

data test;
input x y;
cards;
. 10
. 20
1 30
1 40
2 50
2 60
;
proc report data=test missing spanrows;
  column x y z;
  define x / order order=internal;
  define y / display;
  define z / computed;
  compute z / character length=1;
    if x=. then z="Y";
  endcomp;
run;
😕


  • まず、XをORDER変数にしています。またORDER変数が欠損値のオブザベーションも出力対象とするため、MISSINGオプションを指定。
  • 次にCOMPUTEで「if x=. then z="Y"」として、Xが欠損値のオブザベーションに"Y"のフラグを立てたんですが、、想定では1、2オブザベーション目だけに"Y"が入るはずなのに、4、6オブザベーション目にも"Y"が入っている?!


XをORDER変数にしたことが影響してると思うんですが、リファレンスにこの辺の挙動の説明見つけられませんでした。

ORDER変数(GROUP、ANALYSIS、ACROSS変数とかも?)を参照した処理(IFステートメントや関数など)は想定通りの結果にならないことがあるので、使用は避けたほうが良さそうです。




失敗例2

proc report data=test missing spanrows;
  column x y;
  define x / order order=internal;
  define y / display;
  compute x;
    if x=. then x=2;
  endcomp;
run;
😕


「if x=. then x=2」として、「Xが欠損値」の場合は「X=2」のオブザベーションとして以下のように一塊で出力したかったのに、、


あと、失敗例①を知っちゃうと、なんとなく以下のようになるのでは?と思っちゃいません?(失敗例①で「X=.」と判定されたオブザベーションが、今回「X=2」になると思ってしまった)



ORDER変数(GROUP、ANALYSIS、ACROSS変数とかも?)に対して、COMPUTEで値をいじると、うまく絡み合ってくれなくて、想定外の結果になる場合があるのでご注意ください。

そもそも元の変数値をPROC REPORT上でいじるのって、あまりやらない、というか良くないのかも?




あと未検証ながら


以下リファレンス内の「Restriction」の記載によると、、

よく分からないですが、LAG, DIF, RANUNI, DATETIMEなどの「non-deterministic関数」については、特定条件のときに使わない方が良いらしいです。


COMPUTE、難しいよーー!



2023年5月4日木曜日

PROC SQLでの変数ラベルの挙動


あまり知られていない&需要があるのか分からん機能。
まず例から。

data test;
 label x="AA#AA" y="#BB#BB";
 x=1;
 y=2;
run;

proc sql;
 select * from test;
quit;




PROC SQL限定で働く機能で、
ラベルの先頭をアンダーライン「_」以外の半角記号にすると、その文字をその変数ラベルの改行用文字にできるようです。

📝ポイント
  • ラベルだけで、データ部分には影響しない
  • PROC SQLで見たときに改行されてるように見えているだけで、実際のデータセットの変数ラベルに改行が入るわけではない。
  • 日本語等のマルチバイト文字は改行用文字に設定出来ないと思いますが、リファレンスに書かれていないため不明。

2023年4月22日土曜日

サブセット化IFでありがちな落とし穴


SASプログラマ歴が長いと、みんなこれ経験してるんじゃなかろうか。


「サブセット化IF」自体は以下記事で解説しています。


😕失敗例1: 「_N_」と組み合わせて失敗しがち


各オブザベーションに1~連番をふった変数を作りたくて、以下のように書いたのですが、、

data dt1;
 set sashelp.class;
 if age=12;
 x = _n_;
 keep age x;
run;


変数Xに1~連番を入れたいのに、なんかおかしい。


解説

  • まずデータステップは内部の動きとして、
オブザベーションの数だけ、データステップをぐるぐるとループ(反復)させています。
ループのたびに1オブザベーションずつ読み込んでいるイメージで、「_N_」はそのループ毎に「+1」した番号が入ります。

  • 次に「_N_」は内部の動きとして、
まずSET文・WHERE文で読み込むオブザベーションに対して、データステップがループ(反復)されますが、自動変数「_N_」は、各反復内でいち早く番号がふられます

番号をふった後で、サブセット化IFにより処理が継続されずに出力もされなかったオブザベーションがあると、番号が連番にならなくなります。



📝特に「IF _N_=1 THEN なんかの処理;」みたいな書き方で失敗しがち。

例えば、以下プログラム(中身はめっちゃ意味のないことやってるのであんまり見ないでください)

data dt2;
 set sashelp.class;
 if age=12;
 if _n_=1 then do;
  dcl hash hs(dataset:"sashelp.class");
  hs.definekey("name");
  hs.definedone();
 end;
 if hs.check()=0 then x2=1;
run;


_N_=1 のオブザベーションがたまたまサブセット化IFによって「処理継続・出力」の対象外になっているので、「IF _N_=1 THEN DO; ~END;」の中の処理も動いていません。

上の例ではエラーが出て分かりやすいですが、書き方によってはエラーが出ない場合もあって失敗に気づかない可能性もあるので要注意です。


😕失敗例2: 「END=オプション」と組み合わせて失敗しがち


最後のオブザベーションにフラグを立てたくて、以下のように書いたのですが、、

data dt3;
 set sashelp.class end=_eof;
 if age=12;
 if _eof then y=1;
 keep age y;
run;


フラグ立ってないじゃないか!


これは「END=オプション」の挙動を勘違いしていると起こりやすいです。

「END=オプション」はSET文・WHERE文で読み込まれる最後のオブザベーションに作用しますが、その最後のオブザベーションがサブセット化IFによって「処理継続・出力」の対象外になっているためです。


あと思いつくのが、以下で紹介している「FIRST.BY変数」「LAST.BY変数」も、サブセット化IFと組み合わせると、同様の原理で意図しない結果になりやすいですね。

「FIRST.BY変数」と「LAST.BY変数」で、グループ毎の最初と最後のオブザベーションを特定する。


2023年1月1日日曜日

SASで年賀状2023

 



SASで年賀状をつくりました(ほぼ「SASで年賀状」の焼き直し)
2023年もどうぞよろしくお願いいたします!





年賀状をつくるプログラム (SAS9.4M7で動作確認)

*** 描画用のパーツ ;
data rabbit;
  x1= 0;     y1= 0;
  x2= 0;     y2=-6.5;
  x3=-0.31;  y3= 1.2;
  x4= 0.31;  y4= 1.2;
  x5= 0;     y5= 0.15;
  x6= 0;     y6= 0.7;
  x7=-0.5;   y7= 5;
  x8= 0.5;   y8= 5;
  x9=-0.5;   y9= 5.5;
  x10=0.5;   y10=5.5;
  x11=-0.8;  y11=0.1;
  x12=0.8;   y12=0.1;
run;

*** 描画 ;
title ;
ods graphics on / height=10cm width=14.8cm;

proc sgplot data=rabbit noautolegend;
 styleattrs wallcolor=khaki;
   
 /* Text */
 inset "HAPPY NEW YEAR"  /  position=top textattrs=(color=brown size=31cm);
 inset "20"  /  position=bottomleft textattrs=(color=brown size=140cm );
 inset "23"  /  position=bottomright textattrs=(color=brown size=140cm );
   
 /* rabbit */
 scatter x=x1 y=y1  /  markerattrs=(symbol=circlefilled  size=3cm color=white);
 scatter x=x2 y=y2  /  markerattrs=(symbol=circlefilled  size=4cm color=white);
 scatter x=x3 y=Y3  /  markerattrs=(symbol=circlefilled  size=0.2cm color=black);
 scatter x=x4 y=Y4  /  markerattrs=(symbol=circlefilled  size=0.2cm color=black);
 scatter x=x5 y=y5  /  markerattrs=(symbol=ArrowDown  size=0.5cm color=black);
 scatter x=x6 y=y6  /  markerattrs=(symbol=TriangleDownFilled  size=0.2cm color=black);
 scatter x=x7 y=y7  /  markerattrs=(symbol=CircleFilled  size=1cm color=white);
 scatter x=x8 y=y8  /  markerattrs=(symbol=CircleFilled  size=1cm color=white);
 scatter x=x9 y=y9  /  markerattrs=(symbol=CircleFilled  size=1cm color=white);
 scatter x=x10 y=y10  /  markerattrs=(symbol=CircleFilled  size=1cm color=white);
 scatter x=x7 y=y7  /  markerattrs=(symbol=CircleFilled  size=0.7cm color=mistyrose);
 scatter x=x8 y=y8  /  markerattrs=(symbol=CircleFilled  size=0.7cm color=mistyrose);
 scatter x=x9 y=y9  /  markerattrs=(symbol=CircleFilled  size=0.7cm color=mistyrose);
 scatter x=x10 y=y10  /  markerattrs=(symbol=CircleFilled  size=0.7cm color=mistyrose);
 scatter x=x11 y=y11  /  markerattrs=(symbol=CircleFilled  size=0.4cm color=pink);
 scatter x=x12 y=y12  /  markerattrs=(symbol=CircleFilled  size=0.4cm color=pink);

 xaxis min=-3 max=3  display=none;
 yaxis min=-5 max=1  display=none;
run;