MAXをとるためにORDINALを使う屁理屈

実戦用というよりかは
頭を柔くするパズル感覚、昔やってた「詰めSAS」的な話として見てください。

さて、以下のデータセットがあって、

data A;
X1=1; X2=3 ;X3=2;output;
X1=.;  X2=.  ;X3=2;output;
X1=3; X2=.  ;X3=3;output;
X1=.;  X2=.  ;X3=.;output;
run;









MAXをとれと言われれば一目、こう書きます。

data B;
set A;
 M=max( of X: );
run;









しかし、NOTEがでます。










NOTEを回避するには

data C;
set A;
 if n(of X:) ne 0 then M=max( of X: );
run;

と書くわけですが、結局、引数が全欠損の場合、いずれにせよ
結果は欠損で変わらない。

そうすると、NOTEの有無を除けば「 if n(of X:) ne 0 then 」の部分は完全な無駄手、
結果は同じなのに一手損しているようで気持ち悪いなぁと昔から思っていました。

個人的な感性の話を抜きにしても、
min maxが入れ子になっているとき、かつ各ブロックで全欠損が生じる可能性が許容されている場合、別に出てもいいNOTEを回避するためだけに、いちいち、分解して書かないといけなかったりして面倒なこともあります。

これ、実は以下のように書けば、ifを書かずにNOTEもださずに、同じ結果に
なると思うんですね。

data D;
set A;
 M=ordinal( 3 , of X: );
run;

ordinal関数は、欠損値を含めて、n番目に小さい数をとるという関数です。
nを引数の数に合わせることで、最終番目に小さい数、すなわちそれが最大値という理屈です。

はなから欠損値を含めることを想定した関数なので、引数が全欠損でも仕様範囲内なのでNOTEはでない。
同値があっても、引数の数を指定していれば結果は揺らがない。

最大をとるために、あえて逆に小さい方からみるっていう発想が頭の体操になるなぁと。

対象引数リストが配列なら、ordinalの第一引数にはdim関数を入れちゃえば、より安心なコードになるね。



毎度毎度「SAS 日付 フォーマット 」とかでググるのはもうウンザリなんだ!!って話

日付を「January 1, 1960」(WORDDATE)でだせとか、「昭和35年 1月 1日」(JNENGO)でだせとかってときに毎回、なんのフォーマットあてればいんだっけってなって、だいだい検索してる。

検索すればすぐ出てくるからいいんですけど、何回も何回もデジャブってると、ちょっと面白くない。

以前の記事「カラーリストの出力」
http://sas-tumesas.blogspot.jp/2017/01/blog-post.html
と似た話だけど、SAS使いなら、わかんないことはSASにコードで教えてもらおうぜってことで

以下のコードなんかどうでしょう?


proc sql;
select fmtname
 ,fmtinfo(fmtname,"desc") as 説明
 ,putn(0,cats(fmtname,maxw,".")) as 例
from dictionary.formats
where fmttype eq "F" and first(fmtname) ne "$"
;
quit;

実行すると、その環境で使用可能な全数値フォーマットと
fmtinfo関数で取得したフォーマットの説明、0にそのフォーマットを
あててみた結果がずらっとでます。(画像は一部を適当に切ってます)












































ショートカットキーとか省略形に追加して、すぐ呼び出せるようにしとくと便利

でもね、

アフリカーンス語では金曜日のこと「Vrydag」って言うんだ~。
てかアフリカーンス語ってなんだ?
ググッてみよ~、へ~、とかって遊んでしまって、本末転倒なんですけどね。

中途半端な優しさはいらない。いっそ激しくNOAUTOCORRECT

さて問題。
以下のコードを実行するとどうなるでしょうか?

date a;
sent sashelp.class;
fi sex="女子" the doo;
putit "A";
outputsomething;
endit;
ru;


なんじゃこれ?こんなもん通るかっ!って思ったあなたはまだ甘い!!

WARNINGはでるけど、処理は実行されます。
つまり







































ということ。

スペルミスってレベルを超えてる気がするけど…。

まあ、SASの優しさではあるんだけど、スペルミス混じったコードがそのままになっているのは恥ずかしいことですよね
いっそ一思いにエラーにして俺を叱ってくれ!って思うこともありますよね。


そんなドMには

options noautocorrect;

を送りましょう。


これを使って、さっきのコードを流すと










































気持ちいいくらいに全否定

今更ながら9.4でPROC SQLにMEDIANが集約関数として実装されたよ。

ワシはPROC MEANSつかわんと、要約統計量も全部PROC SQLで書くんじゃい!!
というタイプの人にとって、MEDIANがSQL関数として実装されていないのは大きな障壁でした。

「WARNING: MEDIAN関数が1引数のみを使用して呼び出されました。しかし、SQL集計関数ではありません。SQL集計は行われません。」
に落胆した人も多いことでしょう。

いや、SASなんだから、普通にUNIVARIATEとかMEANS使おうよって気もしますが、一応9.4から対応されましたよ。

proc means data=sashelp.fish median;
var weight;
run;

proc sql ;
select median(weight) as 中央値
from sashelp.fish ;
quit;


















ちなみにUsage Noteでたまたま見つけて、知ったんですが、SQLリファレンスのWhat's Newに記載がないのはなんでなの??


「Usage Note 12133: Prior to SAS® 9.4, the Base SAS® MEDIAN() function has limitations when used in PROC SQL」
http://support.sas.com/kb/12/133.html

outputメソッドって引数に複数指定できたんだねって話

最近、初心に戻ってハッシュオブジェクトのリファレンスを読み返していて初めて知ったこと。

outputメソッドにdataset:を複数指定できる!おぉ、知らなかった。

例えば

data Q1;
X=3;Y="A";output;
X=2;Y="B";output;
X=1;Y="A";output;
X=1;Y="A";output;
X=2;Y="B";output;
X=3;Y="B";output;
run;













といったデータがあって、
data _null_;
set Q1 end=eof;
if _N_=1 then do;
declare hash h1(dataset:"Q1",multidata:"Y",ordered:"Y");
h1.definekey("X");
h1.definedata(all:"Y");
h1.definedone();
end;
h1.ref();
if eof then h1.output(dataset:"OUT1(where=(Y='A'))"
                                       ,dataset:"OUT2(where=(Y='B'))");
run;

とすると

【OUT1】


【OUT2】







となる


お~、そうなんだ~。
複数指定したいと思ったことはあまりないけど、知っておいて損はないですね