省略の極み data;set;run; _LAST_

data A;
 X=1;
run;

data;
 set;
 X=X+1;
run;


上記のコードを実行するとどうなるでしょうか?

2ステップ目はdataの後も何もなく、setの後にも何も指定していません。

結果は









こんな感じになります。

dataで何も指定しないと、勝手に連番でDataXXといったデータセットが実行の度に作成されます。

これについては以前、「どうてもいい話 データセット名をかかずに実行」

で触れていました。完全に忘れていましたが、、。

で、今回はsetで何も指定しないとどうなるかという話で、これは

自動的に

set _LAST_;として解釈され、実行されます。

例えばプロシジャの指定で
proc print;
run;

として実行した場合も

proc print data=_LAST_;
run;

と自動で解釈されて実行されます。

で、この_LAST_ってなにって?なると、直前に作成されたデータセットが自動的に指定されます。


なので、最初のコードで、勝手にDATA1とか作るのではなくデータセットAを上書きしたいんだという場合は


data A;
 X=1;
run;

data _LAST_;
 set;
 X=X+1;
run;

とすれば








って感じです。


_LAST_は使いどころ無くはないです。
マクロの中とかで、データセットの作成順に変わる余地がない場合とか?

それでも、あてにしていたステップでデータセットが作成されず、想定外のものを_LAST_で拾ってしまう可能性もあるので、僕はあまり使ったことないですが。





人真似をして結局失敗。飛び値がある場合の、数字→文字変換

リンクサイト「SAS忘備録」の記事「1行プログラムその3:数値を任意の文字に置き換える。」
http://sas-boubi.blogspot.jp/2014/03/13.html

を読んで、やっぱ凄いなぁと思いました。

で、記事の最後の方で、飛び番の場合は注意とありました。

実際、1='あり' 2='なし' 100='不明'というような変換が必要な場合ってよくあります。

if文とかselect文、或いはフォーマット作ってからそれを当てる以外に書き方ないかなぁと考えました。

data DT1;
 input V1;
 cards;
 1
 100
 2
 999
 ;
 run;









data DT2;
 array A{999} $10.;
  A{1}='なし';
  A{2}='あり';
  A{100}='不明';
  A{999}='もっと不明';
 set DT1;
  if V1^=. then V2=choosec(V1,of A{*});
 keep V1 V2;
run;







おっ、これならchoosec使えんじゃない?と思ったけども

それなら


data DT3;
 array A{999} $10.;
  A{1}='なし';
  A{2}='あり';
  A{100}='不明';
  A{999}='もっと不明';
 set DT1;
  if V1^=. then V2=A(V1);
 keep V1 V2;
run;


これでいいし、そもそもこんなにだらだらコードがかさむなら、if文と変わらないし、
全然1行コードから離れるし、駄目でした。


第3回電王戦の第二局を観覧しに行きます。

完全に趣味の話です。

将棋プログラムや囲碁プログラムが好きです。
将棋も囲碁も現在の思考アルゴリズムは、乱暴に言うとプロ棋士の対局のデータベースを読み込んで、とりうる指し手をそれぞれ得点化する関数を作成し、その関数がはじき出す、高得点な手を指し続けることで勝つ感じです。

またモンテカルロ囲碁という言葉を聞いたことがある方も多いと思いますが、ここ最近の囲碁プログラムの発展は目覚ましく、そのきっかけは2006年にフランスの方がモンテカルロ法を導入したことがきっかけになっています。(モンテカルロ法の囲碁への応用が最初に提案されたのは93年です)


で、先週の3/15日から毎週土曜日に5週連続で、コンピュータプログラム対プロ棋士の戦いが行われています。

その第2戦目を3/22観戦にいきます。

http://ex.nicovideo.jp/denou/

もし、このブログを見られている方の中に、同じく観戦にいかれる方がいれば、ご連絡ください。
SASと将棋を一緒に話せる人を探してます。

あと、SASで将棋や囲碁の思考プログラムを組まれている方がいらっしゃれば、同じくご連絡いただけると幸せです。

データステップ100満開 文字列がどれくらい類似しているかを定量化する

「データステップ100満開」という検索ワードでこのブログが検索されていて、春めいていて素敵だなぁと思いました。

「データステップ100満開」と「データステップ100万回」ってどれぐらい似てるでしょうか?検索で出てくるわけだから、まあ似てるんでしょう。
その似てるさ度合いを数値化してみます。
(Googleとかの検索アルゴリズムは、類似度だけじゃなく事前情報をつかって、なんか統計的な凄いことしてるんでしょう。僕はわかりません)


文字列がどの程度類似しているかを定量化する術は多く研究化されています。僕は全く門外漢なので、さわりだけ。
有名なのが編集距離の概念で、ようはどれぐらい操作を加えたら一致するのかで考え、操作の回数やコスト(1文字替えて一致するなら100ポイントみたいな)合計でもって、似てるっぽさをだそうという感じです。

明らかアルファベットで構成される言語に向いてそうですが、まあそこは置いておいて。

data Q1;
length X Y $50.;
X='データステップ100万回';Y='データステップ100満開';output;
X='データステップ100万回';Y='データステップ1万回';output;
X='データステップ100万回';Y='デデデータステップ100万回';output;
X='データステップ100万回';Y='出た!ステップ100万買い';output;
X='ABC';Y='ABC';output;
X='ABC';Y='AABC';output;
X='ABC';Y='AB';output;
X='ABC';Y='ABD';output;
X='ABC';Y='CAB';output;
X='ABC';Y='AAA';output;
X='ABC';Y='XYZ';output;
X='ABC';Y='ABCABC';output;
X='ABC';Y='A B C';output;
run;

こういうデータがあったとします。

それに3つの関数をかけてみます

data A1;
set Q1;
SP=SPEDIS(X,Y);
CG=COMPGED(X,Y);
CLV=COMPLEV(X,Y);
run;











SPEDIS関数のスコア計算の基準
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000245949.htm

COMPGED関数のスコア計算の基準
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a002206133.htm

COMPLEV関数のスコア計算の基準
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a002206137.htm

だいたい同じ傾向ですが、微妙な順序の違いが面白いですね!



ODS PDFでしおりの名前を自由に設定する  proclabelやproc printのcontents pageby等

ODSを使えば、いろんなファイル形式に出力できますが、PDFは見栄えが綺麗で、単純な指定で、
結構それっぽく?仕上がるので、便利です。基本文字切れしないし。

そんなPDFの小技の一部です。

data LB;
subjid='A';visit='サイクル1';val=100;output;
subjid='A';visit='サイクル2';val=100;output;
label SUBJID='症例番号' VISIT='サイクル' VAL='検査値';
run;

data VITAL;
subjid='B';visit='サイクル1';val=50;output;
subjid='B';visit='サイクル2';val=70;output;
label SUBJID='症例番号' VISIT='サイクル' VAL='検査値';
run;

適当なデータがあって

ods pdf file="display1.pdf";
 proc print data=lb;
  by visit;
 run;
 proc print data=vital:
  by visit;
 run;
ods pdf close;

こんな感じで実行すると












こんな感じですね。

今回注目なのは
左側にある「しおり」の部分です。

「Printプロシジャ」―「サイクル=サイクル1」-「データセット WORK.LB」とかって
勝手になってます。

SAS知っている人に見せるなら、解ってもらえるのですが、以前まったく知らない人に
出力結果を速報として(親切で)あげようと思ってods pdfでぱっと作って差しあげた際
その人から「Printプロシジャ」ってなに?WORK.ってなんなの?とか聞かれて
1つ1つ説明した挙句、「わかんないから手で普通の言葉に直して、あと最終レポートも早く作ってね」と言われて、まあ、そりゃそうかと思いつつ、ポチポチ手で直した寂しい記憶があります。


でもそういう時

ods pdf file="display2.pdf";
 ods proclabel='臨床検査';
 proc print data=lb
  contents="データ元:LB";
  by visit;
  pageby visit;
 run;

 ods proclabel='バイタルサイン';
 proc print data=vital
  contents="データ元:VITAL";
  by visit;
  pageby visit;
 run;
ods pdf close;


こんな感じで、色々工夫できます。

先に実行結果から


ods proclabelでプロシジャ出力の最上位のしおりを指定できます。
byはby変数がそのまましおりになりますが、pageby等で、ページを分けることができます。
contents=で、元データのしおりを制御できます。

実は、もっと山ほどオプションがあって、階層いじったりもできるのですが、とりあえずここまで。










ユーザー定義フォーマットの確認やオプションの確認など

自分用のメモです。

オプションとユーザー定義フォーマットを確認したい時にもう100回くらい、書き方忘れるのでここに書きます。

proc options define;
run;

で使用可能なオプションの説明がログに山ほどでる。

proc options option=firstobs define;
run;

とoption=オプション名で指定すればそのオプションだけ調べれる。

けど大体、ヘルプかリファレンスみるかwebで調べる方が詳しくわかる。


ユーザー定義フォーマットの中身を確認するfmtlibオプションを、いつも忘れるので。


proc format;
 value SEX 1='男'
           2='女';
run;

proc format lib=work fmtlib;  
run;









データセットに吐き出す場合は

proc format lib=work cntlout=A1;  
run;


データセットから呼び込む場合はcntlinで。


ods graphicsで作成される複合グラフを1つ1つのプロットに分解する話

データは適当です。

data Q1;
X=1;Y=4;output;
X=2;Y=5;output;
X=5;Y=8;output;
X=6;Y=9;output;
X=9;Y=12;output;
X=11;Y=10;output;
X=22;Y=26;output;
X=12;Y=15;output;
run;

ods graphicsが有効になっている状態で統計プロシジャをまわすと

ods graphics on;
proc reg data=Q1;
model Y = X;
run;
quit;
ods graphics off;























まあ、いっぱいプロットがでてきますよね。

試しに一つ開いてみると






















こういう複合プロットってありますよね。
凄い見栄え綺麗なんですが、複合状態じゃなく、個々に独立したプロットとして出したい時が
あるとします。

その場合


















だしたい複合プロットの名前を見てから

ods graphics on;
proc reg data=Q1 plots(only)=DiagnosticsPanel(unpack);
model Y = X;
run;
quit;
ods graphics off;

こんな感じで、(unpack)とつけてやります。そうすると























プロットが分解されます。


ここからはおまけですが、

例えば、ODS graphicsで作成される自動プロットも、内部的にはGTLで処理
されているわけなので、コードで再現可能なわけです。

例えば




















さっき、分解したプロットのうちの一つですが、これってどんなコードで作られているんだろうかと
思ったら

また














プロパティみるのですが、今度は「テンプレート」って部分をみます

















あとはテンプレートの表示タブから












階層をたどり












































目当てのテンプレートを発見したらダブルクリックすれば



こんな感じで、さっきのグラフを作っているGTLを入手できます。

ちなみに僕は45度線の書き方がわからない時に、この方法でゲットしました。


ATTRN関数でwhere条件にかかったオブザベーション数を取得する

オブザベーション数をマクロ変数に格納する方法から、SCL関数を僕が勉強していく話
http://sas-tumesas.blogspot.jp/2014/03/scl.html

でATTRN関数にNOBSを指定してオブザベーション数を取得する話を紹介しました。

関連する話題として、open関数にwhereデータセットオプションをくっつけた場合について
紹介します。


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


data A1;
   DSID=open(("A(where=((X > 3))"),'i');
   NOBS=attrn(dsid,"NOBS");
   NLOBSF=attrn(dsid,"NLOBSF");
run;







といった感じでNOBSオプションはあくまでデータセットのオブザベーション数を返し
NLOBSFとすると、実際に抽出されるオブザベーション数がとれます。
例の場合3レコードが抽出でかからなかったということがよくわかります。

で、最後に、ご存知の方がいらっしゃればご教示いただきたいのですが
NOBS NLOBSF 以外にNLOBSというのも指定できるのですが、Helpを読んだりしても使い方が
よくわかりませんでした。



頭の体操 集合に対する抽出

例えば

data Q1;
TEAM='A';ID='aさん';VAL=90;output;
TEAM='A';ID='bさん';VAL=35;output;
TEAM='A';ID='cさん';VAL=42;output;
TEAM='A';ID='dさん';VAL=56;output;
TEAM='A';ID='eさん';VAL=68;output;

TEAM='B';ID='fさん';VAL=40;output;
TEAM='B';ID='gさん';VAL=58;output;
TEAM='B';ID='hさん';VAL=62;output;
TEAM='B';ID='iさん';VAL=52;output;
TEAM='B';ID='jさん';VAL=68;output;

TEAM='C';ID='kさん';VAL=90;output;
TEAM='C';ID='lさん';VAL=88;output;
TEAM='C';ID='mさん';VAL=62;output;
TEAM='C';ID='nさん';VAL=32;output;
TEAM='C';ID='oさん';VAL=38;output;

run;



















のようなデータがあったとします。

A B Cの3チームで、チーム内には複数のメンバーがいて変数VALには何らかの得点が
入っているとします。


ここで、チームの70%以上のメンバーが50点以上の得点であるグループのTEAMを抽出して
データセットにしたいとします。

さて、どうしますか?

if VAL>=50 then FL=1;とかデータステップで一度付与してからfreqなんかで集計するのも手ですが、この手の問題については、やはりべらぼうにSQLが強いです。集合指向言語の肩書は伊達じゃないです。


つまり

proc sql ;
create table A1 as 
 select TEAM
 from Q1
 group by TEAM
 having count(*)*0.7<=sum(case when VAL>=50 then 1 else 0 end);
quit;

で詰んでるんですね。






【追記】
しまった!真偽ルールを使えば

proc sql ;
create table A1 as 
 select TEAM
 from Q1
 group by TEAM
 having count(*)*0.7<=sum(VAL>=50);
quit;

これでいけるんだった。くそっ





オブザベーション数をマクロ変数に格納する方法から、SCL関数を僕が勉強していく話

以前、
SASデータセットのオブザベーション数をマクロ変数に格納する方法_call symput とsql select into: ①単純な1マクロ変数の作成


【訂正追補】SASデータセットのオブザベーション数をマクロ変数に格納する方法_call symput


でオブザベーション数をマクロ変数にいれる話をしました。

最近、リンクさせていただいたブログ「SAS Utility」の中で、全く別のアプローチでオブザベーション数を取得していて、感動しました。


記事タイトル「マクロ %nobs


SCL関数をマクロに組み込んでいるわけですね。

そういえば、同じくリンクブログの「僕の頁 <SASと臨床試験と雑談と>徒然なるままにSAS暮らし」で
fetchobs関数」の紹介をされていました

これもそっち系の技術ですね。
なんか、感覚的にfetchobs関数の処理ってハッシュオブジェクトの処理と似てません?面白いです。

実は僕はSCLの類はさっぱりで、定型文を呪文的に使うことがあるぐらいだったので、今から勉強していきたいと思います。

例えば

data Q1(label='データセットラベル');
 X=1;Y=2;Z=3;output;
 X=1;Y=2;Z=3;output;
 X=1;Y=2;Z=3;output;
 X=1;Y=2;Z=3;output;
run;









data Q2;
 A=1;B=2;output;
 A=1;B=2;output;
run;







ここで、この2つのデータセットの変数の数、オブザベーション数、データセットについているラベル
帰属するライブラリ名を取得することを考えます。




 data A1;
   DSID=open('Q1');
   VARN=attrn(dsid,"NVARS");
   OBS=attrn(dsid,"NOBS");
   DSLABEL=attrc(dsid,'LABEL');
   LIB=attrc(dsid,'LIB');
  output;
   DSID=open('Q2');
   VARN=attrn(dsid,"NVARS");
   OBS=attrn(dsid,"NOBS");
   DSLABEL=attrc(dsid,'LABEL');
   LIB=attrc(dsid,'LIB');
  output;
run;





open関数を使ってデータセット名を指定するとそのデータセットがオープン状態になって操作するための固有IDみたいなんがとれるってことですよね?そしたらそれを第一引数として、戻り値が数字になるような属性をとる場合はATTRN関数、文字の場合はATTRC関数で、第二引数に欲しいパラメータを指定するわけですね。

なるほど、なるほど、通常のデータステップで使用する場合は、ステップ終了時にオープン状態が自動解除されるので、特に気にしなくていいけど、マクロ内で%sysfuncで実行する場合は、オープンのままになってしまうから、close関数を使うわけですね。

ちょっとずつわかってきました。





selectに定数を入れてwhenに変数をいれる

本で人様のコードを見てはっとする。
XかYかZのいずれかが2の時に出力するという際に

data Q1;
 X=1;Y=2;Z=3;output;
 X=3;Y=4;Z=5;output;
 X=3;Y=5;Z=2;output;
 X=1;Y=1;Z=3;output;
run;

data A1;
set Q1;
select (2);
 when (X,Y,Z) output;
 otherwise;
end;
run;

そっか、selectに定数入れれるのですね、考えてみると当たり前のような気もするけど
自分だと自然にでてこない書き方ってありますね。


背筋が凍る、厄介でおっかない小数点誤差の話

恐らく、誰しもが一度は経験していることだと思いますが、小数の計算というものはとてもやっかいです。
他の言語は詳しくないのでわかりませんが、SASにおいても、ある程度知っておかないと、直感では理解不能な現象に直面します。

今、以下のようなデータセットがあるとします。

data Q1;
A=0.2;B=0.1;output;
A=0.3;B=0.2;output;
run;






AからBを引いて、Cという変数に割り当てます

data Q2;
set Q1;
 C=A-B;
run;






それがどおした?当たり前じゃないかという感じですが、
知っている人は既にピンときているはず。


Cが0.1であれば変数FLに'Y'、それ以外は'N'をいれます。

data Q3;
 set Q2;
 if C=0.1 then FL='Y';
 else FL='N';
run;

当然、Cは2レコードとも0.1なわけだから、FLはYになると思いきや、、、








とまあ、こういうことになるんです。

これは、見た目に同じ0.1でも実は内部的に10の何乗分の1ぐらいの、超小さい部分で誤差が生じているんですね。
なんで、0.2-0.1と0.3-0.2みたいな小学生でもできる計算で誤差が生じるのかというと、SASが内部的に2進法に変換して計算しているため、パターンによって10進法に戻すときに微細な誤差がでちゃうんですね。
ごめんなさい、感覚で覚えているので、詳しい理屈は詳しい方に聞いてください。

見た目、両方、0.1じゃん!と思った方は

data Q4;
set Q3;
D=C;
format D HEX16.;
run;

のように例えば16進数のフォーマットを当ててみると





あ、確かになんか違う!って感じです。


この現象が往々にして猛威をふるいます。

if文などの条件分岐がこけたり、コンペアが一致しなかったり、ソート順がおかしく見えたりするわけです。

対応策としては、roundで

data Q5;
 set Q2;
 if round(C,0.00001)=0.1 then FL='Y';
 else FL='N';
run;

適当に余裕をもって丸めるとかの






原始的処理になります。

まあ、コンピューターってそういうもんだとは思いますが、何とかならないですかね、、、。





SAS Power and Sample Sizeの話

以前、「ODS Graphics Editorの話 (特別なライセンスなし、SAS/GRAPHでできる便利な機能)」
http://sas-tumesas.blogspot.jp/2014/02/sasgraph-ods-graphics-editor.html

で、マウスとクリック操作で、グラフが作れて、それを作成するコードを表示できるという話をしましたが、今回は例数設計です。

SASの例数設計はpowerプロシジャでできます。実際、コードとしては指定するものを指定して実行するだけですので、指定の意味を理解していれば簡単ですし、SASのヘルプ等をみながら書けば、プログラム的には簡単なので、別にGUIで作成する機能いるんかな??と思わなくもないですが一応紹介まで。

コマンドでの起動の仕方がわからないので、インストール時にチェック入れて、
「N」字のアイコンがないかたについては







再度追加インストールするか、確かwebからも入れれたと思います。

で、クリックすると、なんもない画面がでてきるので、とりあえず










新規ファイルを作成します






















そしたら、どんな検定の例数設計なのか、聞いてくるので、それを選びます。
1標本t検定を選びます






そしたら、必要例数が欲しいんか、検出力なのかを聞いてきます























必要例数を選択して、あとは求められるままにパラメータを入れて、次へ次へクリックします



























最終的にパラメータを入れ終わったら右端の下あたりある「Calculate」ボタンを押すと





































powerプロシジャのアウトプットと同じ結果がでます。

必要例数は16例ですね


それで、左側にタブがついていて、例えば「SAS Code」を選ぶと、やっぱり



こんな感じでSASコードに落としてくれます。


勝手にグラフでビジュアル化もしてくれるので、ちょっとひねったことする時、こっちでやってコードを確認するのも手だと思います。

この「SAS Power and Sample Size」のマニュアルはWeb上に転がっているので、詳しい使い方はそちらを参照ください。




同じデータセット内の変数同士の差異を検出する。compare関数とproc compareのwithステートメント

例えば

data Q1;
length X Y Z $10.;
X='ABC';Y='ACC';Z='ABC';output;
X='BBC';Y='BBC';Z='BBC';output;
X='CBC';Y='CBC';Z='CBB';output;
run;







Xの値とYの値、Xの値とZの値、Yの値とZの値を比較して同一かどうかを検証
したいとします。


方法はたくさんありますが、たとえば、compare関数を使えば

data A1;
set Q1;
XvsY=compare(X,Y);
XvsZ=compare(X,Z);
YvsZ=compare(Y,Z);
run;








のようになり、0であれば同一、数字は最初に差異が発見された位置を示しています

data A2;
set Q1;
YvsX=compare(Y,X);
ZvsX=compare(Z,X);
ZvsY=compare(Z,Y);
run;

のように指定をひっくり返すと


基準が逆転するので正負も逆転します。


で、こういうことをcompareプロシジャでやってみたい場合は

proc compare data=Q1;
 var X X Y;
with Y Z Z;
run;

のようにvarとwithの指定順を同期させることで可能です。縦に見ると、何と何を比較しているか
わかりやすいです。

結果の抜粋は以下です。




















ODSへの対応や、差異のデータセットへの吐き出しについてcompareプロシジャには
まだ改良の余地があると思うので、次期バージョンでもいいからてこ入れして欲しいなと思います。



【解答求む】フォーマットがついた変数を新規変数に割り当てても解除される話

今更ながら、とても基本的な部分で質問なのですが
たとえば、以下のようなデータセットがあって

data Q1;
 X=18993;
 format X yymmdds10.;
run;





Xは数値で、日付フォーマットがついてます。

data A1;
 set Q1;
 Y=X;
run;

とすると





YにXの値が割り当てられますが、フォーマットは継承されません。

YにもXと同じフォーマットをあてたい時

data A1;
 set Q1;
 Y=X;
 format Y yymmdds10.;
run;

としてました。

まあ、同じ内容の変数を1つのデータセットの中に作る処理なんて、意味ないのでほぼやったことないせいですが、今までこのことに疑問を感じたことはありませんでした。

これって他にやりようないのでしょうか?
フォーマットを継承するオプションや関数をご存じの方がいらっしゃればご教示いただきたいです。


なぜ、そんなことをいまさら思ったのかというと
例えば、同じような処理でも

proc sql noprint;
 create table A2 as
  select X 
        ,X as Y
    from Q1
    ;
quit;

とすると





となってフォーマットは継承されているわけです。ただ、この場合は、割り当てているのではなく
変数Xをコピーしてをリネームしているという感じなので、フォーマットが同じで当たりまえといえば
当たり前なわけです。継承しているとはいわないでしょう。


じゃあ、その理屈でいくならばと思って以下のようにすると

data A3;
 set Q1;
 set Q1(rename=(X=Y));
run;





できました。
できましが、これ、変数追加するためだけに2回セットしてて、効率悪いですよね。

こんなことばかり、いつも思いついては、考え込んでしまいます