2014年7月30日水曜日

%INCLUDEで複数のSASプログラムを一括実行する


あるパスにいくつかのSASプログラムファイルがあったとします。

TEST1.sas
TEST2.sas
TEST3.sas


以下のように書くと、該当のSASプログラム達を実行してくれます。
 %inc  "パス\TEST1.sas"  "パス\TEST2.sas"   "パス\TEST3.sas";



パスが長ったらしいのであれば、FILENAMEステートメントを通すとスッキリします。
 filename  MYPGM  "パス";
 %inc  MYPGM  (TEST1.sas  TEST2.sas TEST3.sas) ;



またワイルドカード機能もあり、
アスタリスク(*)を使えば、例えば以下のようにファイル名に”TEST”がつくSASプログラムファイルを一括実行するような事も出来ます。
 %inc  "パス\TEST*.sas" ;



2014年7月27日日曜日

「SASユーザー総会2014」に行ってきました。


今年はCDISCに関する各社の取組などの発表が多く、熱気を感じました。

気のせいか発表者もいつもより多かったような。
データステップ100万回」や「僕の頁」の方達も発表されていました。
高校生がJMPなどを使ったトンボと生物多様性についての発表をされたりもしてて、みんなほんとにすごいなぁ。


いつも発表を見てる側ですが、、せめてこのブログで役に立ったりSAS面白いなとか思ってくれるような小技とかを紹介していけたらなーとか思ってます。


それにしても今回は見たい発表が重なったりして見れなかったのも多く残念でした。
TEDみたいに動画配信していつでも見れるようにしてほしいです。


帰りにアンケートに答えたらSASマニアが喜ぶ?オリジナルの折りたたみ傘をもらえました。

2014年7月23日水曜日

SQLプロシジャ入門7:レコードを追加する【INSERT】


SQLプロシジャによるレコードの追加方法を紹介します。



サンプルデータ

data DT1;
   A=1; B="AA"; output;
   A=2; B="BB"; output;
run;

data DT2;
   A=5; C="AAA"; output;
   A=6; C="BBB"; output;
run;

データセットDT1
 A  
B
  1
  AA    
  2
  BB 

データセットDT2
 A 
 C
  5
  AAA   
  6
  BBB 



構文1

proc sql;
   insert into DT1 (A,B)
   values (3,"CC");
quit;


データセットDT1
 A  
B
  1
  AA    
  2
  BB 
  3
  CC 


基本構文
  INSERT INTO 対象のデータセット  ( 対象の変数 , … )
  VALUES ( 格納したい値 , … )




構文2

proc sql;
   insert into DT1
   values (4,"DD");
quit;


解説
構文1から( 対象の変数 … )部分省略するとデータセットに格納されてる変数順にVALUESで指定した値を格納していきます。




構文3

proc sql;
   insert into DT1 (A,B)
   select A,C from DT2;
quit;


解説
・SELECT,FROMなどで抽出したレコードを、INSERT INTOで指定したデータセットに追加します。

・INSERTとSELECTで変数名が異なっていても順番で対応づけられます(上記の例ではDT1の変数BにDT2の変数Cの値を格納しています。)

・ちなみに構文2と同様、青字部分を省略して書くことも出来ます。

・今回の例ではログにWARNINGが表示されます。理由は記事の下の方に記載した「注意点」を参照ください。



構文4

proc sql;
   insert into DT1
   set A=7,  B="EE";
quit;


解説
「SET 変数名=格納したい値, …」 という書き方もあります。


[注意点]
追加する値が、もとのLENGTHよりも大きい場合、もとのLENGTHにあわせて文字を切った状態で追加されます。(ログにWARNINGも表示される)




SQLプロシジャ入門記事一覧
1.変数を選択する【SELECT】
2.レコードを並べ替える【ORDER BY】
7.レコードを追加する【INSERT】
8.レコードを削除する【DELETE】

2014年7月19日土曜日

値をマクロ変数に格納する「CALL SYMPUTX」その2




前回の続きです。ちょっとした落とし穴を紹介。


まず以下の例を見てみましょう。

* マクロ変数を削除 ;
%symdel X  / nowarn;

* call symputxでマクロ変数を作りログに出力 ;
%macro TEST;
     data _null_;
         call symputx("X", "abcd");
     run;
%mend;
%TEST;

%put "&X";

ログ
"abcd"



ログを見てみると、問題なくマクロ変数を参照できてますね。
では次のプログラムはどうでしょうか?

* マクロ変数を削除 ;
%symdel X2 X3 / nowarn;

* call symputxでマクロ変数を作りログに出力 ;
%macro TEST2;
     %let X2=1;
     data _null_;
         call symputx("X3", "abcd");
     run;
%mend;
%TEST2;

%put "&X3";

ログ
WARNING: 記号参照X3を展開していません。


WARNINGが出て参照できませんでした。
最初のマクロとの違いと言えば「%let X2=1;」が追加されたくらいですが、実はこれが影響してます。


超ざっくり説明すると、「%let X2=1;」はマクロTEST2の中で定義してるマクロ変数で、いわゆるローカルマクロ変数(以下参照)です。

  ローカルマクロ変数:
   そのマクロ内でしか参照できないマクロ変数(マクロが実行終了すると消去される)

  グローバルマクロ変数:
   どの場所からでも参照できるマクロ変数


マクロの中でひとつでもローカルマクロ変数があると、同マクロ内に書かれたCALL SYMPUTXの結果もローカルマクロ変数となります。(グローバルマクロ変数になる場合もある。詳細はこちらを参照)





もしグローバルマクロ変数に変えたい場合、はじめに「%GLOBAL マクロ変数名;」を追記すればOK。
逆にローカルマクロ変数にしたい場合は、%LOCALステートメントを追記します。

またSYMPUTにはなくSYMPUTXのみの機能として以下のように書けちゃいます。

  call symputx("X3", "abcd" , "G") ;

  "G" …  グローバルマクロ変数にする。
  "L"  …  ローカルマクロ変数にする(マクロ外で実行するとグローバルマクロ変数になる)


この点もCALL SYMPUTXを使うメリットだと思います。


2014年7月18日金曜日

値をマクロ変数に格納する「CALL SYMPUTX」その1




CALL SYMPUTは知ってるけど、CALL SYMPUTXって何?という人も多いかと思います。


共に変数値や値をマクロ変数に格納することが出来ます。
ではどう違うのかというと、、


例1: 文字値をマクロ変数に格納する
data _null_;
   length X $10.;

   X = "abcd";
   call symput("MAC1", X);
   call symputx("MAC2", X);
run;

%put "&MAC1";
%put "&MAC2";

ログ
"abcd      "
"abcd"

変数Xの値をマクロ変数MAC1とMAC2に入れてから%PUTでログにその値を展開してます。
  • CALL SYMPUTはLENGTHの余りを半角スペースとして保持してしまうのに対して、
  • CALL SYMPUTXは両端の半角スペースを取り除いてくれます。


例2: 数値をマクロ変数に格納する
data _null_;
   Y = 123.456;
   call symput("MAC3", Y);
   call symputx("MAC4", Y);
run;

%put "&MAC3";
%put "&MAC4";

ログ
"     123.456"
"123.456"

  • CALL SYMPUTでは数値を文字変換してマクロ変数に格納するので、ログに「NOTE: 以下の箇所で数値を文字値に変換しました」と出力されます。またマクロ変数値の先頭に半角スペースが入ります。
  • CALL SYMPUTXでは文字変換&両端の半角スペースを取り除いて、かつログには文字変換した旨のメッセージは出力されません。


例3: 数値をマクロ変数に格納する際の注意
data _null_;
   Y = 111.111111112;
   call symput("MAC5", Y);
   call symputx("MAC6", Y);
run;

%put "&MAC5";
%put "&MAC6";

ログ
"111.11111111"
"111.11111111"

マクロ変数に格納したら、数値が丸められてしまいました。
小数部分の桁が多くて勝手な丸めがまずい場合は、自分で適切な文字値に変換した値をマクロ変数に格納すると安全です。


次回に続く。


2014年7月14日月曜日

SQLプロシジャ:値をマクロ変数に格納するINTO句の小技


まずは基本的なところから。

* サンプルデータ ;
data DT1;
input A$;
cards;
AA
BB
CC
;

 AA 
 BB
 CC


上記変数Aの値をマクロ変数に入れたい場合、SQLプロシジャを使うと以下のように書きます。

* マクロ変数に格納 ;
proc sql noprint;
  select A  into :A1 - :A3
  from DT1;
quit;

* マクロ変数値をログに出力 ;
%put &A1;
%put &A2;
%put &A3;

ログ
AA
BB
CC


データセットの1行目の値を&A1に、2行目を&A2に、3行目を&A3に入れてます。
もしレコードが100行あったら「:A1 - :A100」と書けば&A1~&A100に各レコードの値が入ります。


そしてSAS9.3からの新機能。

proc sql noprint;
  select A  into :A1 -
  from DT1;
quit;

これだけで&A1, &A2, &A3…というようにレコードの数だけ勝手にマクロ変数を作ってくれます。



📝以下、落とし穴についての記事も要確認!

・数値をマクロ変数に格納すると丸められる可能性がある
・フォーマットが割り当てられた変数だと、フォーマットを当てた値がマクロ変数に格納される。

SQLプロシジャのINTO句で、前方に余計な半角スペースが入る場合がある。
・余計な半角スペースが入ってしまう場合の小技を紹介してます


2014年7月9日水曜日

FCMPプロシジャと行列計算


FCMPプロシジャはユーザー定義関数を作成するだけでなく、簡単な行列計算をしたい場合にも便利です。

簡単なサンプルとして、以下の連立方程式をFCMPプロシジャによる行列計算で求めてみたいと思います。








ちなみに行列で解を求める式はこんな感じでしょうか。




まずは式の数字部分をデータセット化しておきます。
data DT1;
input A B C D;
cards;
1 1 1 6
2 4 3 19
5 3 2 17
;
run;

 A  B  C  D 
 1 1 1  6
 2 4  3 19
 5 3 2 17


FCMPプロシジャで行列により解を求めます。
proc fcmp;

   *** ① 計算用の配列を定義 ;
   array _ABC [3, 3]   / nosym;
   array _D     [3, 1]   / nosym;
   array _INV  [3, 3]   / nosym;
   array _OUT [3]      / nosym;

   *** ② 計算するデータセットを配列に格納 ;
   rc = read_array("DT1", _ABC, "A","B","C");
   rc = read_array("DT1", _D    , "D");

   *** ③ 行列計算して解を出す ;
   call inv(_ABC, _INV);        * 逆行列 ;
   call mult(_INV, _D, _OUT);  * 行列の積 ;

   *** ④ 結果をデータセットに書き出す ;
   rc = write_array("OUT1", _OUT, "RESULT") ;

run;

データセットOUT1
 RESULT 
 1
 2
 3

 x=1, y=2, z=3 という結果が得られました。


解説
処理のおおまかな流れは、計算用のデータセットを配列に格納 → その配列を使って行列計算 → 出た解をデータセットに吐き出す。。といった感じです。


①まず行列計算で必要になる配列を定義しておきます。

NOSYMをつけると配列に変数を割り当てる必要がなくなります。
(DATAステップのARRAYでいうところの一時配列_TEMPORARY_みたいなもの)


READ_ARRAY関数でデータセットDT1を配列に格納します。

READ_ARRAY("読み込むデータセット",  "読み込んだデータを格納する配列",  "読み込む変数1",  "読み込む変数2"…)


③読み込んだ配列に対して、行列計算して解を出します。

逆行列    … CALL INV( 配列,  結果を格納する配列 )
行列の積 … CALL MULT( 配列1, 配列2,  結果を格納する配列 )


WRITE_ARRAY'関数で解をデータセットOUT1に格納します。

WRITE_ARRAY("出力後データセット名",  "出力する配列",  "出力時の変数名1",  "出力時の変数名2"…)



2014年7月3日木曜日

関数の中でOFを使うと変数の指定が楽になる例と応用例。



たとえば変数A1~A5の合計を求めたい時、

VAR = sum(of A1-A5);

と書きます。よくあるOFの使い方ですが以下のように、、

VAR2 = sum(A1, A3, A5);

VAR2 = sum(of A1 A3 A5);

変数名の間のカンマ(,)を省略したい場合にOFを使うことができます。


このカンマ省略機能が生かされる例として以下の例を見てみましょう。

%macro M_SUM( DSNAME, VARS );

  data &DSNAME;
    A=1;
    B=1;
    C=1;
    D = sum(of &VARS);
  run;

%mend;
%M_SUM( DT1, A B C );
%M_SUM( DT2, A C );


マクロを実行する際の引数の指定が楽になってますね。
OFを使わないとカンマをQUOTEするために%STR関数等を使う必要がありました。

%M_SUM( DT1, %str(A, B, C) );
%M_SUM( DT2, %str(A, C) );