2014年6月27日金曜日

MERGEにBYがない場合にWARNINGを出す


MERGEにBYを入れ忘れてしまうことがたまにあるかと思います。
こういった場合にWARNINGを出す「MERGENOBYオプション」があります。

options  MERGENOBY = WARN ;

data A;
  A=1;
run;

data B;
  A=1;
run;

data C;
  merge A B;
run;


ログ
WARNING: BYステートメントがMERGEステートメントに指定されていません。




初期状態(WARNINGを出さない)に戻す場合は以下の通り。

options MERGENOBY = NOWARN ;



プログラム開発中だけでもこのオプションを入れておくほうがプログラミング時のミスを軽減できるかと思います。


2014年6月26日木曜日

外部ファイルをいっぺんに読み込んで連結する。


(注意点もあるので、最後までご覧ください)


いくつかのCSVファイルがあったとします。

test1.csv
001,a,11
002,b,22

test2.csv
003,c,33
004,d,44


filenameステートメントで以下青文字部分のようにファイルを列挙すると、まとめて読み込んで連結したデータセットを作ってくれます。

filename  FL  ("パス\test1.csv"  "パス\test2.csv") ;

data OUT1;
  length A B $10. C 8.;
  infile FL dsd truncover;
  input A B C;
run;

 B   C  
 001  a 11
 002 b  22
 003 c 33
 004 d 44


またワイルドカード機能もあり、
アスタリスク(*)を使えば、例えば以下のようにファイル名に”test”がつくCSVファイルをいっぺんに読み込むことも出来ます。


  filename  FL  "パス\test*.csv" ;



注意!
今回紹介した方法では、読み込むファイルのヘッダー部分を「firstobsオプション」でスキップさせたい場合に、うまく動かないので、以下参照ください。



以下で別の方法も紹介してるので、参考までに。。(こちらの方法だと、読み込むファイルのヘッダー部分を「firstobsオプション」でスキップ出来る)


2014年6月25日水曜日

FCMPプロシジャ限定で使えるIF式


FCMPプロシジャ限定で、IFステートメントの代わりにIF式が使えます。


まず通常のIFステートメントの書き方。
proc fcmp;
   A = 1;
   if  A=1 then B=1;  else B=0;
   put B;
run;

上記と同等の処理を、IF式を使って簡略化できちゃいます。
   B=  if A=1 then 1 else 0 ;


2014年6月23日月曜日

UPDATEステートメントでオブザベーションをまとめる

UPDATEステートメントの本来の使い方から外れますが、私がよくやる具体例を紹介。
まずサンプル作りから。

*** 患者背景データ ;
data DT1;
length SUBJID $3.;
input SUBJID$ AGE SEX$;
cards;
001 51 F
002 25 M
003 30 F
;
run;
proc sort data=DT1; by SUBJID; run;

 SUBJID  AGE  SEX 
 001 51 F
 002 25  M
 003 30 F

*** 有害事象データ ;
data DT2;
length SUBJID $3. AETERM $20.;
input SUBJID$ AETERM$ ;
cards;
001 発熱
001 不眠
003 腹痛
;
run;
proc sort data=DT2; by SUBJID; run;

 SUBJID 
AETERM 
 001
 発熱    
 001
 不眠 
 003
 腹痛 

DT1は患者ID(SUBJID)毎の年齢(AGE)や性別(SEX)など患者情報が入っているデータです。
DT2は患者ID(SUBJID)毎の治験実施時の有害事象(AETERM)が入っているデータです。

ここで以下のように書くと、、
data OUT3;
   update  DT1 
               DT2 (keep=SUBJID in=X);
   by SUBJID;
   if  X  then AEFLG=1;
run;


 SUBJID  AGE  SEX  AEFLG 
 001 51 F 1
 002 25  M .
 003 30 F 1

有害事象が発現してるかどうかの変数AEFLGを患者背景データに作ることができました。
UPDATEステートメントによって有害事象データがBY値毎に1行にまとまってくれるとこが便利です。

仕組みについては「UPDATEステートメント入門」を参照。
もっと面白いことができるのでまた今度紹介したいと思います。

2014年6月18日水曜日

UPDATEステートメント入門


最近UPDATEステートメントにはまってます。
「データステップ100万回(UPDATEステートメントを使ってみる)」から興味を持ち始めたのですが色々面白いことが出来そうなので、こちらでも基礎から応用例まで紹介したいと思います。


基本構文
data  結果データセット ;
  update  マスターデータ  トランザクションデータ ;
  by   BY変数;
run;


MERGEと似たような動作で、マスターをトランザクションデータで更新します。
異なる点は以下4点で、UPDATEステートメントを使いこなすうえで重要なルールとなります。

① BYステートメントは必須。
② マスター側はBY値が重複するオブザベーションがあったらダメ。
③ トランザクション側はBY値が重複していてもOK。(トランザクション側の値を上から順にマスターへ更新していく)
④ トランザクション側の値が欠損値の場合は更新しない。(MERGEだと欠損値になる)



分かりづらいと思うのでまず以下のサンプルを見てみましょう。

*** マスターデータ作成 **********;
data DT1;
input A B;
cards;
1 1
2 1
3 .
;
run;

*** トランザクションデータ作成 ****;
data DT2;
input A B;
cards;
1 .
2 20
2 30
3 20
4 20
;
run;

*** トランザクションデータを使ってマスターを更新 *****;
data DT3;
   update DT1 DT2;
   by A;
run;


  A    B  
   1    1
   2  30 
   3  20 
   4  20 



具体的には以下のような処理の流れになります。
上の方で説明したUPDATEステートメントのルール①~④と照らし合わせて見てみましょう。

2014年6月17日火曜日

変数のINFORMAT, FORMAT, LABELを消す方法。




よく質問を受けるのでまとめたいと思います。

*** サンプルデータ ;
data DT1;
attrib
  A label="ラベル1"  informat=8.   format=8.
  B label="ラベル2"  informat=$10. format=$10.
  C label="ラベル3"  informat=8.   format=8.
;
run;

proc contents data=DT1; run;



CONTENTSプロシジャで変数定義情報を見てみると、余計なINFORMAT(入力形式)やFORMAT(出力形式)などが付いてることがあります。これらを消す方法を紹介したいと思います。

data DT2;
   set DT1;

   *** 変数AのINFORMATを消す ;
   informat  A ;

   *** 変数AのFORMATを消す ;
   format  A ;

   *** 変数Aのラベルを消す ;
   label  A= ;
run;

INFORMATとFORMATを消す処理はSETの後に書かないと機能しません。



全変数まとめて処理したい場合は_ALL_が使えます。

data DT2;
   set DT1;

   *** 全変数のINFORMATを消す ;
   informat  _ALL_  ;

   *** 全変数のFORMATを消す ;
   format     _ALL_ ;

   *** 全変数のラベルを消す ;
   attrib      _ALL_  label=" " ;
run;

_ALL_でラベルを消したいときだけATTRIBを使わないと動きません。



データセットを直接更新したい場合は、DATASETSプロシジャで以下のように書いてもいけます。

proc datasets lib=WORK nolist;
  modify DT1;
  attrib _all_ label=" ";
  attrib _all_ format=;
  attrib _all_ informat=;
quit;


データセットに直接処理するので、容量のでかいデータセットの場合はこちらの方が処理時間は早いと思います。



イコールがいる場合といらない場合があったりと、微妙に書き方の統一がとれてないので、どう書くんだっけ?となってしまいます。。
SASのバージョンアップにともなってこの辺の仕様統一もしてほしいなぁ。


2014年6月13日金曜日

変数指定は「V100-V1」のように逆にもできる。


変数がV1、V2・・・V100とあったとき、「keep V1-V100」みたいに範囲指定して書けることはわりと知られています。
これはさらに「keep V100-V1」のように逆に指定することもできます。

この逆指定で処理が楽になる例を以下にあげてみました。

サンプルデータのNO1からNO3の中で最後の値を取ってきたい場合、
COALESCE関数と変数逆指定の組み合わせで簡単に出来てしまいます。

*** サンプルデータ作成 ;
data DT1;
input SUBJID:$3. NO1 NO2 NO3;
cards;
001 100 95 110
002 56  50 .
003 90 .  .
;

*** 変数の逆指定 ;
data DT2;
  set DT1;
  LAST = coalesce( of NO3-NO1 );
run;

 SUBJID   NO1   NO2   NO3   LAST 
  001    100   95   110   110 
  002   56  50   .  50
  003   90   .   .   90

変数の範囲指定を関数で行う場合「of」を先頭につけるのを忘れずに!


2014年6月5日木曜日

SQLプロシジャの小技:番号で変数を示す


SASというよりSQLの基本技です。以下のプログラムを見てみましょう。

*** サンプルデータ作成 ;
data DT1;
input A B$;
cards;
1 d
1 c
2 b
3 a
;

*** 番号で変数を表す例  ;
proc sql;
   select A, count(*) as C
   from  DT1
   group by 1
   order by 1, 2;
quit;


GROUP BYやORDER BYで数字を指定してますね。
これはSELECTで指定された変数の何個目かを表してます。

たとえば「ORDER BY 1,2」は、SELECTで指定された変数の1個目、2個目の順にソートするという意味になります。


注意点として、あとでSELECT句の変数順を変えた場合は、GROUP BYやORDER BYに影響がないか確認するのを忘れずに。

2014年6月4日水曜日

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


知ってるという人も多いと思うけど、SQLでも一部のデータセットオプションを使うことができる。

便利だけど、あまり使われてない気がします。
ということで、SQLでデータセットオプションを使うメリットを紹介します。

サンプルデータ作成

data DT1;
   retain  A1 - A5 1;
run;

データセットDT1
A1   A2   A3   A4   A5
 1     1     1      1      1


SQLでデータセットオプションを使う

問題①データDT1をもとにDT2を作成。ただし変数A3はB3にrename。
結果データセットDT2
A1   A2   B3   A4   A5
 1     1     1      1      1

*** 通常のSQL文 ;
proc sql;
 create table DT2 as
 select A1,
           A2,
           A3 as B3,
           A4,
           A5
 from DT1 ;
quit;

*** データセットオプションを使う場合 ;
proc sql;
 create table DT2 as
 select *
 from DT1 (rename=(A3=B3)) ;
quit;


問題②データDT1をもとにDT3を作成し、変数A1~A3を格納。
結果データセットDT3
A1   A2   A3
 1     1     1

*** 通常のSQL文 ;
proc sql;
 create table DT3 as
 select A1,
           A2,
           A3
 from DT1 ;
quit;

*** データセットオプションを使う場合 ;
proc sql;
 create table DT3 as
 select *
 from DT1 (keep=A1-A3) ;
quit;


このように、通常「select *」が使えない場合でも、SASのデータセットオプションを組み合わせることで使えるようになる。

今回はほんの一例で、他にも色々スッキリした文を書ける場合があります。
データセットオプションは「create table」「from句」で使用することが出来る。
SASとSQLのいいとこ取りです!

2014年6月2日月曜日

FREQプロシジャ のWARNINGメッセージ「期待度数が5より小さくなっています。」の制御方法



まず以下のプログラムを見てみましょう。

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


*** Fisherの正確検定 ;
proc freq data=DT1 noprint;
   tables A*B / exact ;
   weight C / zeros;
   output out=OUT1 exact ;
run;


ログ
WARNING: セルの75% (表 : A * B)において、期待度数が5より小さくなっています。カイ2乗検定は妥当な検定でないと思われます。



カイ2乗検定で「期待度数が5未満のセル」の比率が20%より大きい場合に上記のようなログが出ます。



カイ2乗検定を行う場合、状態を把握するためにも、このログは出力させておいたほうが良いと思います。
しかし今回の例では、はじめからFISHERの正確検定だけをやりたいのに、自動でカイ2乗検定の結果も出るため、このWARNINGは今回は見ないため、なくしたい。

そんな時にNOWARNオプションでこのログを制御できます。

*** Fisherの正確検定 ;
proc freq data=DT1 noprint ;
   tables A*B / exact  nowarn ;
   weight C / zeros;
   output out=OUT1 exact  ;
run;



またカイ2乗検定を出すときに「WARN=OUTPUT」と記述すると、、

*** カイ2乗検定 ;
proc freq data=DT1 noprint ;
   tables A*B / chisq   warn=output ;
   weight C ;
   output out=OUT1 chisq ;
run ;


「期待度数が5未満のセル」の比率が20%より大きいかどうかのフラグ変数を出力データセットに作成してくれます。
(SAS環境によって異なりますが「カイ2乗値のWarning」みたいなラベルでフラグ変数が作成されます)