2014年1月31日金曜日

行番号を取得する方法




基本的な方法や、あまり知られていない方法をいくつか紹介したいと思います。



*** サンプルデータ ;
data DT1;
input V$;
cards;
a
b
c
d
e
;
*** 行番号を取得 ;
data DT2;
  set DT1;

  * 方法① ;
    OBSN1 + 1;

  * 方法② ;
    OBSN2 = _N_;

  * 方法③ リファレンスに記載のない関数のため非推奨 ;
    OBSN3 = monotonic();
run;



解説

方法①
変数名 + 1」と書くと、前のオブザベーションの変数値を引き継いで、「+1」していってくれる。

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


方法③ (リファレンスに記載のない関数のため非推奨)
MONOTONIC関数は方法①と同じような挙動をします。



注意事項

方法①~③は厳密には挙動が異なります。
たとえば以下のプログラム実行後、それぞれの方法での結果を見ると異なっています。

data DT3;
  set DT1;
  if V in ("a","c","e");

  * 方法① ;
    OBSN1 + 1;

  * 方法② ;
    OBSN2 = _N_;

  * 方法③ ;
    OBSN3 = monotonic();
run;



これは行番号を振るタイミングが異なるため。

方法①と③:
SET文・WHERE文で読み込まれたオブザベーションに対して番号を振るのですが、、
番号を振る前にサブセット化IF「if  V  in  ("a","c","e")」が実行されていますよね。
このサブセット化IFにより処理が継続されたオブザベーションに対して、番号をふっています。

方法②(自動変数「_N_」):
まずSET文・WHERE文で読み込むオブザベーションに対して、データステップがループ(反復)されますが、自動変数「_N_」は、各反復内でいち早く番号がふられます
番号をふった後で、サブセット化IFにより処理が継続されなかったオブザベーションがあると、番号が連番にならなくなります。


MONOTONIC関数の注意点を追加。

monotonic関数でアイタタタ(データステップ100万回)


2014年1月24日金曜日

条件式(IF/WHERE)のちょっと効率的な書き方。


IF/WHEREステートメントには以下の真偽ルールが存在する。

変数値または式の結果が

 ・ 0、NULLの場合 ・・・ 「0.false
 ・ 上記以外 ・・・ 「1.true

として評価される。




解説

これはどういう事なのか、以下のサンプルプログラムを実行してみると分かる。

*** サンプルデータ作成 ***********;
data TEST1 ;
  input V1 @@;
cards ;
1 3
;








*** サンプルプログラム ***********;
data TEST2;
  set TEST1;
  if  V1  then  V2=1;
run;



真偽ルールに従い、変数「V1」が「true」となったものに対して、「V2=1」となっている事が分かる。



活用例①

*** サンプルデータ作成 ******;
data DT1;
  do A=1 to 5 ;
     output ;
  end ;
run ;

data DT2 ;
  do A=2 to 4 ;
     output ;
  end ;
run ;

data DT3 ;
  do A=1 to 3 ;
     output ;
  end ;
run ;

DT1

DT2
 
DT3


上記3データセットを変数「A」をキーにマージしたいとする。
その後たとえば、、
「A」の値が、「DT2」にあって「DT3」にないものを抽出したい場合、
IN=オプションを使って、通常以下のように書く。

data OUT1;
  merge DT1
            DT2 (in=INDT2)
            DT3 (in=INDT3)
  ;
  by A;
  if  INDT2=1 and INDT3=0 ;
run;




これは真偽ルールを元に書き直すと、
 if  INDT2  and  ^INDT3 ;
となる。


「^」は「NOT」の略で、真偽ルールが逆になる。つまり、、
  ・0、NULLの場合 ・・・ 「1.true
  ・上記以外 ・・・ 「0.false
となる。

この真偽ルールと論理演算子「^」の組み合わせは便利で、
使用する場合は以下記事を参照下さい。
論理演算子「^」「^^」によるフラグ変数の作成


活用例②

*** サンプルデータ作成 ******;
data DT4 ;
  input B$ ;
cards;
ABCD
EFGH
;
run;



上記データセットに対して、
変数「B」に"C"という文字が含まれているレコードを抽出したい場合、
INDEX関数を使って、通常以下のように書く。

data OUT2 ;
  set DT4 ;
  if index(B, "C") > 0 ;
run ;



これを真偽ルールに従って書き直すと、
 if index(B, "C") ;
と書くだけでよい。


まとめ

「0/1」や「NULL/1」と入ってる有無変数やフラグ変数に対して有効的に働き、
論理演算子「^」と組み合わせることでスマートに書けるので便利です。

2014年1月21日火曜日

条件式(IF/WHERE)におけるINオペレータの小技




以下のようなデータセットがあったとする。

data DT1;
    do V=1 to 6 ;
        output;
    end;
run;



そこから、変数「V」が「1, 3, 4, 5」のどれかだったら、
レコードを抽出したり何らかの処理を行いたい場合、以下のように書くことが出来ます。

data DT2;
    set DT1;
    if  V in (1, 3, 4, 5) ;
run;



さらに「コロン」を使うと便利です。
「3 : 5」とコロンを挟むことで、「3~5の整数」と範囲指定することができます。

data DT2;
  set DT1;
  if  V in (1, 3 : 5) ;
run;


IFじゃなくて WHERE V in (1, 3 : 5);
とWHEREに置き換えて実行すると、どのような内部処理が行われてるかログに表示されます。

それによると 「WHERE  (V=1)  or  ( (V=INT(V))  and  (V>=3 and V<=5) ) ;
みたいな長ったらしい文に置き換えて内部処理してることが分かります。

「1または3~5の整数」 という条件になってますね。



注意とまとめ

コロンによる指定は整数限定(「0.1 : 0.5」みたいな小数指定は無理)。

個人的には、項目番号でレコードを管理してる臨床検査値などのデータセットを集計するときに活躍してます。



2014年1月16日木曜日

オブザベーション数を取得する自動マクロ変数「&SYSNOBS」




SAS9.3から、かなり使える自動マクロ変数「SYSNOBS」が追加されました。

このマクロ変数にはDATAステップやPROCステップで最後に閉じられたデータセットから読み取られたOBS数が格納されます。

以下に具体的な動作をまとめてみました。



マクロ変数「NOBS」にOBS数を格納する例。

*** 具体例① **************;
data DT1;
  V1=1; output;
  V1=2; output;
run;

%let NOBS=&SYSNOBS;

解説
データセット「DT1」が作成され、OBSは2行。
よって、「SYSNOBS」は「2」となる。



*** 具体例② **************;
proc sort data=DT1 out=DT2;
  by V1;
  where V1=1;
run;

%let NOBS=&SYSNOBS;

解説
データセット「DT1」をもとに「where V1=1」の1OBSが抽出されてソートされ、「DT2」が作成される。
よって、「SYSNOBS」は1」となる。



*** 具体例③ **************;
data _NULL_;
  set DT1;
  where V1=1;
run;

%let NOBS=&SYSNOBS;

解説
注意が必要。
データセット「DT1」をもとに「where V1=1」の1OBSが抽出される。
しかし、「data _NULL_」としているので、データセットは作成されない。
よって、ここで最後に閉じられたデータセットは「DT1」なので、「SYSNOBS」は「DT1」のOBS数である「2」となる。



*** 具体例④ **************;
proc sql noprint;
  select *
  from DT1
  where V1=1;
quit;

%let NOBS=&SYSNOBS;

解説
注意が必要。
こちらも上記同様、SQL文の中で「create table」を記述していないので、最後に閉じられたデータセットは「DT1」となり、「2」となる。



*** 具体例⑤ **************;
data V1 / view=V1;
  set DT1;
run;

data _NULL_;
  set V1;
run;

%let NOBS=&SYSNOBS;

解説
注意が必要。
viewは特殊なデータなので、OBS数は取得できず、「-1」が返される。




まとめ

あと仕様なのか、環境やバージョンによって挙動は異なるかもですが、データセットをダブルクリックで開いてすぐ閉じてから、
「%put "&SYSNOBS";」
と書いて実行しログに出力された値を見ると「1090」という謎値が返される。

独特な動きをするけど、そこを理解することでかなり使える。

2014年1月10日金曜日

最小値・最大値を求めるMIN・MAX関数



各オブザベーションごとに「指定した変数の中での最小値・最大値を求めるMIN・MAX関数」をご紹介



構文


最小値を求める
MIN( 変数1, 変数2, 変数3 ・・・)

最大値を求める
MAX( 変数1, 変数2, 変数3・・・)






/* テストデータ作成 */
data test1;
input a b c;
cards;
. 3 2
5 1 4
;

/* MIN, MAX関数を使ってみる */
data out1;
  set test1;
  vars_min = min( a, b, c );
  vars_max = max( a, b, c );
run;