2016年8月26日金曜日

集計結果から必要なものだけ表示する 【ODS SELECT編】



例えば、univariateプロシジャを実行すると色々な統計量が出力されます。
この出力から必要なものだけに絞って出力する方法の紹介です。


①まずは「ODS TRACE」で出力オブジェクトの名前を調べる
ods trace on;
proc univariate data=sashelp.class;
 var age;
run;
ods trace off;


ログ


集計プロシジャを「ods trace on;」 と 「ods trace off;」で囲って実行すると、ログに上のような表示が現れます。
ログに出てきた「名前」のところにあるキーワードを覚えておきましょう。
たとえば今回出力したいのが「モーメント」と「基本統計量」であれば、「Moments」と「BasicsMeasures」という名前を覚えておきます。




②「ODS SELECT」で集計結果を絞る(注意点あり)
ods select Moments  BasicMeasures;
proc univariate data=sashelp.class;
 var age;
run;
ods select all;

結果ビューア



今度は集計プロシジャを「ods select  名前;」 と 「ods select all;」で囲い直します。
名前のところに先ほどのキーワード「Moments」「BasicsMeasures」を当てはめます。

実行すると上の通り、見事欲しい結果だけ出力することが出来ました。


📝 注意点①

2つのunivariateプロシジャに対して「Moments」と「BasicMeasures」のみ出力したい場合、以下の書き方はNG

* NG: ダメな書き方 ;
ods select Moments  BasicMeasures;
proc univariate data=sashelp.class;
 var age;
run;

proc univariate data=sashelp.class;
 var height;
run;
ods select all;

実行してみると分かりますが、
ods selectで設定した「Moments」「BasicMeasures」は、1個目のunivariateプロシジャにしか適用されていません。


以下のようにプロシジャ毎にods selectを書く必要があります。

* OK ;
ods select Moments  BasicMeasures;
proc univariate data=sashelp.class;
 var age;
run;
ods select all;

ods select Moments  BasicMeasures;
proc univariate data=sashelp.class;
 var height;
run;
ods select all;


📝 注意点

以下のプログラムを実行するとWARNINGが出て、うまくいきません。
何故でしょうか?

* NG: ダメな書き方 ;
proc reg data=sashelp.class;
 model height = age;
run;

ods select Moments  BasicMeasures;
proc univariate data=sashelp.class;
 var age;
run;
ods select all;

ログ
 WARNING: 出力'Moments'は作成されていません。出力オブジェクト名、ラベル、
          パスが正しく記述されているかを確認してください。また、
          要求した出力オブジェクトを作成するために、適切なプロシジャオプションが使われ
          ているかも確認してください。たとえば、
          NOPRINTオプションが使われていないことを確認してください。
 WARNING: プロシジャステートメントの終わりを検出したので、
          現在のODS SELECT/EXCLUDE/OUTPUTステートメントをクリアしました。
          対話型プロシジャ(終了するにはquit;をタイプします)が終了していないかもしれません。


これは対話型プロシジャを使う場合に起こる落とし穴です。
(対話型プロシジャについては、「RUNとQUITの違い」をご覧ください。)


今回の例で使用しているregプロシジャは対話型プロシジャです。
「quit;」と書くか、別のdataステップ・procステップが現れるまで、regプロシジャは起動したままになります。
なので、起動したままのregプロシジャに対して、ods selectが適用されてしまい、おかしな結果になってしまったわけです。

この場合「quit;」を入れるだけで解決です。

* OK ;
proc reg data=sashelp.class;
 model height = age;
run;
quit;

ods select Moments  BasicMeasures;
proc univariate data=sashelp.class;
 var age;
run;
ods select all;



📝 注意点

ごくごく一部のプロシジャ(gplotプロシジャ等)は、実行する度にods traceで得られる出力オブジェクトの名前が変わるので注意!


2016年8月24日水曜日

PUTステートメントで、変数値の先頭にある半角スペースが消えてしまう




以前いただいた質問です。以下の例をご覧ください。

*** サンプルデータ ;
data DT1;
  A="aaaaa"; output;
  A="  bbb"; output;
  A="  ccc"; output;
run;

  A  
aaaaa 
  bbb 
  ccc



上の例で2~3行目の変数値の先頭には半角スペースがあります。
これを以下のようにPUTでログに出力してみると、、

*** 変数Aをログに吐いてみる ;
data DT2;
    set DT1;
    put A;
run;

ログ
aaaaa
bbb
ccc

先頭の半角スペースが消えてしまいます。このスペースを残すにはどうしたら良いでしょうか?



解決案


(変数にはFORMATが割り当てられていない前提です)
data DT2;
   set DT1;
   put +(kverify(A," ")-1) A;
run;

ログ
aaaaa
  bbb
  ccc



解説
「KVERIFY関数」と 「カラムポインタコントロール機能」 を使ってみました。
(KVERIFYについては以下記事で紹介しているので参照下さい)
・ VERIFY(KVERIFY)関数の紹介


   put +(kverify(A," ")-1) A;


上の青文字部分では「KVERIFYで半角スペース以外の文字が最初に出現する位置-1」、つまり先頭の半角スペースの数を求めています。

 
   put +(kverify(A," ")-1) A;


PUTステートメントで 「+(式)」とすると、式の結果の値(数値)の分だけポインタを移動させてくれます。
つまり変数Aの先頭の半角スペースの分だけ右にポインタを移動させてから、Aの値を出力しています。




2016年8月18日木曜日

SQLプロシジャ内でもSAS関数が使える



MIN、MAX、PUT、などなどデータステップで使っているSAS関数は、SQLプロシジャでも普通に使えます。

  • 一部の関数または関数の機能が使えない場合もある
  • パススルー機能によってDBMSに送信するクエリにも使えない
  • 引数はANSI SQL standard guidelineおよびISO SQL standard guidelineに従う必要がある



* サンプルデータ作成 ;
data DT1;
input A B C;
cards;
1 2 0
2 4 3
;
run;

* SQLプロシジャでSAS関数を利用する ;
proc sql;
   select min(A,B,C) as MINVAL
   from DT1;
quit;

上記ではSASのMIN関数を使ってレコードごとに変数A,B,Cの中の最小値を求めています。


では以下の例はどうでしょうか。
proc sql;
   select min(A) as MINVAL
   from DT1;
quit;

ここで使っているMIN関数は、SQLの集計関数です。
(つまり変数Aの全レコード中の最小値を求めています。)

MIN、MAX、SUMなど、SAS関数とSQL集計関数で同じ名前を持つものがある場合、
引数の数でどちらの関数かを判断しています。

たとえば、
MIN(A,B,C)」 というように引数を複数かけば、SAS関数として解釈し、
MIN(A)」 というように引数が1つであれば、SQL集計関数として解釈されます。


まさにSASとSQLのいいとこどりって感じですね。


関連記事
SQLプロシジャでデータセットオプションを使うメリット。


2016年8月17日水曜日

マクロ言語入門12:補足




マクロ言語入門は今回で最終回になります。
あとはよく使う機能として以下の記事に目を通して頂くとバッチリです。

・ 値をマクロ変数に格納する「CALL SYMPUTX」その1
・ データステップ外で関数を使えるようにする「%SYSFUNC」その1



最後に
マクロはすごく便利ですが、使い方には注意が必要です。
例えば、以下のようなプログラムは、可読性が落ちてしまうかもしれません。

・ 「マクロ引数がいっぱいあるけど、どういう引数なのかコメント等による説明がない」
・ 「マクロをネストしまくっている」
・ 「%IFで何回も条件分岐している」




2016年8月15日月曜日

「put _all_」は動くのに、「put _numeric_」や「put _character_」は動かない。


以下のように 「PUT _ALL_;」 で全ての変数値をログに出力する事が出来ます。

* サンプルデータ作成 ;
data DT1;
   A = 1;
   B = "aaa";
   C = 2;
   D = "bbb";
run;

* 変数値の出力 ;
data _NULL_;
   set DT1;
   put _all_;
run;

ログ
A=1 B=aaa C=2 D=bbb _ERROR_=0 _N_=1

「_ERROR_」 や 「_N_」 などSASが内部で持っている自動変数も出力されます。


同じ要領で 「PUT _NUMERIC_;」 で全数値変数、「PUT _CHARACTER_;」 で全文字変数を出力することが出来そうですが、、
data _NULL_;
   set DT1;
   put _numeric_;
   put _character_;
run;

ログ
ERROR: _character_を変数名として使用できません。
ERROR: _numeric_を変数名として使用できません。

ERRORが出てしまいます。
こういう書き方無理なのかというと、以下のように書けばいけます。


全数値変数をログに出力 (自動変数は出力されない)
data _NULL_;
   set DT1;
   put (_numeric_)(=);
run;

ログ
A=1 C=2


全文字変数をログに出力 (自動変数は出力されない)
data _NULL_;
   set DT1;
   put (_character_)(=);
run;

ログ
B=aaa D=bbb



関連記事
PUT (変数名) (出力時の表示法) という書き方

2016年8月13日土曜日

「FILENAME TEMP」 によるSASプログラムの生成と実行



以下の構文で一時的なファイル参照を作れます。
(ファイル参照が割り当てられている間だけ存在する。)


構文


    FILENAME  ファイル参照名   TEMP ;


📝
LRECLシステムオプションをいじってる場合か、z/OS環境の場合では、FILENAMEステートメントにいくつかオプションを追加しないと挙動が異なるかも。その辺の設定については各自要確認



使用例①

filename TEST temp;

data _NULL_;
    file TEST;
    put "data  A;" ;
    put "run;" ;
run;

%inc TEST / source2;
filename TEST;


filename TEST temp;

まずTESTという名前で一時的なファイル参照を作ります。


data _NULL_;
    file TEST;

一時ファイルを出力先に設定。


    put "data  A;" ;
    put "run;" ;
run;

PUTステートメントで、SASプログラム文 「data A;」 「run;」 を一時ファイルに出力。


%inc TEST / source2;

%INCで一時ファイル内のSASプログラム文を実行。SOURCE2を指定するとログに実行時のプログラム文が出力されます。


filename TEST;

ファイル参照をクリア。



使用例②

data DT1;
    A="AA"; output;
    A="BB"; output;
    A="CC"; output;
run;

  A  
  AA  
  BB
  CC

filename TEST temp;
data _NULL_;
    file TEST;
    set DT1;
    put "data " A "; run;" ;
run;

%inc TEST / source2;
filename TEST;

変数Aの値を使って以下のSASプログラムを生成しています。

data AA; run;
data BB; run;
data CC; run;


単純な例ですが、色々出来そうな事が分かると思います。

2016年8月10日水曜日

マクロ言語入門11:演算評価 【%EVAL、%SYSEVALF】



%EVAL関数」と「%SYSEVALF関数」を紹介していきます。





以下のプログラムと実行ログをご覧ください。

%put  10 + 200 ;

ログ
10 + 200

 「10 + 200」 を計算して 「210」 とログに出力されることを期待しましたが、実際は文字がそのまま返されています。
このような場合、%EVAL関数や%SYSEVALF関数が使えます。




構文

  %EVAL(   演算内容   )

  • 中に書かれた演算を実行する。ただし整数のみ。
  • 割り算等で結果が小数になる場合は、小数部分が破棄されてしまう。
  • 「2 = 2.0」や「a < b」のように値に小数や文字を含む場合、文字値として評価を行います。
    (つまり「2」と「2.0」は異なる文字値として評価される)





  %SYSEVALF(   演算内容   )

  • 中に書かれた演算を実行する。浮動小数点の演算が可能。
  • 浮動小数点の演算後、BEST32形式に変換された値が返される。







%EVAL関数の例
%put %eval( 10 + 200 ) ;
%put %eval( 1 < 2 ) ;
%put %eval( 2 = 2.0 ) ;

ログ
210
1
0

補足 ・・・ 「1 < 2」 のような論理式の場合、論理式が真(true) の場合 「1」が返され、偽(false) の場合 「0」が返されます。



%SYSEVALF関数の例
%put %sysevalf( 0.1 + 0.2 ) ;
%put %sysevalf( 2 = 2.0 ) ;

ログ
0.3
1





記事一覧

1. マクロ変数とは
2. マクロの登録と実行
3. 定位置パラメータ
4. キーワードパラメータ
5. クォート処理
6. クォート処理2
7. ループ処理
8. 条件分岐処理
9. ドット
10. &&
11. 演算評価
12. 補足



2016年8月8日月曜日

【SAS入門】コメントステートメント


以下のプログラムをご覧ください。

data DT1;
  A=1;
run;

proc print data=DT1;
run;


ここで以下青字部分のように処理内容等のコメントを挿入すると、パッと見で何をしているのか分かり易くなります。

* テストデータ作成 ;
data DT1;
  A=1;
run;

* 出力 ;
proc print data=DT1;
run;


このコメント部分を 「コメントステートメント」 といい、この中に書かれた文字は、ただの文字として解釈されます。



コメントステートメントの構文

以下3つの書き方があります。



  *  コメント   ;


落とし穴もあるので以下記事も要チェック。



  /*  コメント   */


動作環境によっては、1~2列目に「/*」を入力すると、SASプログラムまたはセッションの終了として扱われてしまう場合があるようです(私自身Windows環境でそのような状況に遭遇したことがなく、リファレンスにどのような環境での話か記載されていないため詳細不明)




  %*  コメント   ;


ペアマッチしないクォーテーションを入れるとSASがバグるので注意
NG: 「%* "コメント ;」「%* 'コメント ;」
OK: 「%* "コメント" ;」「%* 'コメント' ;」




蛇足

コメントステートメントの書き方は個性が出ます。
例えば以下のように書いてみたり、、

*================================;
* テストデータ作成 ;
*================================;
data DT1;
  A=1;
run;

*================================;
* 出力 ;
*================================;
proc print data=DT1;
run;


何気に人のプログラム見るとき、コメント部分みるの好きです。

2016年8月4日木曜日

変数ラベルを取得するVLABEL関数と実践例


VLABEL関数で変数ラベルを取得することが出来ます。
(変数にラベルが割り当てられていない場合は、変数名が返されます)



構文


   VLABEL( 変数名 )






* サンプルデータ作成 ;
data DT1;
    label A="あああ";
    do A=1 to 3;
       output;
    end;
run;
 A (あああ)  
   1  
   2 
   3 


* 変数Aのラベルを取得 ;
data DT2;
    set DT1;
    length  LAB  $50.;
    LAB = vlabel(A);
run;
 A (あああ)  
  LAB   
   1    あああ  
   2   あああ 
   3   あああ 



実践例

実際質問を頂いてVLABEL関数が活躍した例を紹介します。


以下は患者毎に、該当する疾患(A1~A3)に「1」を入れているデータです。

* サンプルデータ作成 ;
data DT1;
label NO="患者ID" A1="高血圧" A2="高脂血症" A3="糖尿病";
input NO$ A1 A2 A3;
cards;
001 1 . 1
002 . . .
003 . 1 .
;
run;
 NO (患者ID)  
 A1 (高血圧)  
 A2 (高脂血症)  
 A3 (糖尿病)  
   001     1     .     1  
   002    .     .     .  
   003    .     1     .  


このデータから以下のように 「該当する疾患名をカンマで区切って文字化した変数A」 を作りたいとします。
 NO (患者ID)  
 A1 (高血圧)  
 A2 (高脂血症)  
 A3 (糖尿病)  
 A  
   001     1     .     1   高血圧, 糖尿病 
   002    .     .     .    
   003    .     1     .   高脂血症 


解決案
data DT2;
   set DT1;

   length A $200.;
   array AR(*) A1 A2 A3;

   do i=1 to dim(AR);
       if AR(i)=1 then A=catx(", " , A, vlabel(AR(i)) );
   end;

run;
 NO (患者ID)  
 A1 (高血圧)  
 A2 (高脂血症)  
 A3 (糖尿病)  
 A  
   001     1     .     1   高血圧, 糖尿病 
   002    .     .     .    
   003    .     1     .   高脂血症 



上記プログラムを理解するうえで参考になりそうな記事

配列(ARRAYステートメント)入門
区切り文字をつけて変数値同士を結合する【CATX関数】



2016年8月2日火曜日

0オブザベーションの時、1行追加して「該当データなし」みたいな値を入れる方法



サンプルデータ

data DT1;
   length A $30.;
   delete;
run;

           A          



やりたい事

上記のようにデータセットが0オブザベーションだったら、「該当データなし」みたいな値を入れた行を追加したい。

A
  該当データなし 



解決案

(プログラム中の「NOBS=オプション」は正しく動かない状況もあるので、下の各リンク記事も要確認!)

data OUT1;
   length A $30.;
   if _OBS=0 then do;
        A = "該当データなし";
        output;
   end;

   set DT1 nobs=_OBS;
   output;
run;

A
  該当データなし 

0オブザベーション以外の時は「該当データなし」の行は追加されません。


解説

以下リンク記事を読んでいただくと8割がた上記処理の理解につながると思います。

 ・ NOBS=オプションの性質 【オブザベーション数の取得】
 ・ 暗黙のOUTPUTステートメント

補足すると、SETするデータセットが0OBSの場合、SETステートメントの後にどんな処理を入れても作成されるデータセットも0OBSとなってしまいます。(データステップの性質上)

なので、0OBSにされる前に、先に青字のプログラムで「該当データなし」の行をOUTPUTしてから、最後にSETステートメントを実行するようにしています。 なんかパズルみたいですね。



その他

NOBSの注意事項
行削除の落とし穴


2016年8月1日月曜日

NOBS=オプションの性質 【オブザベーション数の取得】




「NOBS=」はSETステートメントで使えるオプションです。まず構文から見ていきましょう。




構文

SET  データセット  NOBS = 適当な一時変数名 ;

  • SETしたデータセットのオブザベーション数を一時変数に格納
  • 一時変数なので、作成したデータセットには残らない
  • WHERE等の文があっても、抽出前のオブザベーション数しか取得できない
  • SASビューとか、TAPE, XMLのようなシーケンシャルエンジンとか、特殊なデータではオブザベーション数の取得に対応していない場合があるので注意
  • その他、落とし穴もあるので、下部の方に貼った注意点の記事リンクも要確認!




* サンプルデータ ;
data DT1;
   do X=1 to 3;
       output;
   end;
run;

* オブザベーション数の取得 ;
data DT2;
   set DT1 nobs=_OBS;
   OBS = _OBS;
run;

  X   
 OBS  
   1    3
   2    3
   3    3





NOBS=オプションの性質


NOBS=オプションは、データステップ内の他のプログラムが実行される前に働きます。
以下のプログラムをご覧ください。


data _null_;
   put _OBS;
   stop;
   set DT1 nobs=_OBS;
run;

ログ
3


set DT1 nobs=_OBS;」で一時変数_OBSにオブザベーション数を格納しています。
その前の 「put _OBS;」で、_OBSの値をログにputしてから、「stop;」で、データステップをstopしています。


stopしてるので、その下の「set DT1 nobs=_OBS;」は実行されないんですが、NOBS=オプションだけは他のプログラムが実行される前に既に働いています。

つまり、「SETステートメントは実行しないけど、オブザベーション数だけ先に取ってくる」 という事をやってのけています。




NOBS=の注意点

行削除の落とし穴
http://sas-boubi.blogspot.jp/2014/05/blog-post_20.html




NOBS=の性質を利用したテクニック

【訂正追補】SASデータセットのオブザベーション数をマクロ変数に格納する方法_call symput
http://sas-tumesas.blogspot.jp/2013/10/sascall-symput.html(データステップ100万回)


データステップ内で、色々なデータセットのオブザベーション数を取得する
http://sas-boubi.blogspot.jp/2015/06/blog-post_26.html