The Nameless City

何故か製薬やSAS関連のブログ、の予定。

SASプログラムを特定ファイルに展開して実行する方法

前のエントリのコメント欄で希望のあった、

call executeについても、わかりにくいですね。putでコード生成して、%includeで実行って方法、聞いたことはありましたが、まだ自分でやったことはありませんでした。ぜひコード例を紹介していただけたら嬉しいです。

SASマクロプログラムの要諦 - The Nameless City

の話。

生成されるプログラムを予めテキストファイルに出力しておき、それを%includeで読み込む。

話としては全く難しくはない。


データセットの中身をファイルに出力する方法は幾つかあるが、
そのウチの一つに、dataステップ内でfileステートメントで出力先を設定し、putで出力する方法がある。

書き出し。

通常、putステートメントは、Log出力に指定した内容あるいは中身を出力する。

data _null_ ;
    put 'Hello! World' ;
run ;
1    data _null_ ;
2        put 'Hello! World' ;
3    run ;

Hello! World
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.14 秒
      CPU時間            0.01 秒


「何も指定していない場合には」。
つまりは、指定する事によって、出力先は変更可能である(この、変更可能である、という所が要諦の一つになる。カンのいい人は、「ストリーム出力だ!」とか、「じゃ、出力デバイス指定出きんじゃね?」とか考えれば、回答が直ぐに思いつくだろう)。

ata _null_ ;
    file "%sysfunc(sysget(temp))\test.txt" lrecl=32767;
    a='Hello! World' ;
    put a a ;
run ;

fileステートメントは、まあ使った事のない人は幾らかいると思うが、infileステートメントの逆だと考えてればいい。通常はテキストファイルを決まった形式で書き出す、ぐらいで考えて使えば良い。
fileステートメントの細かいオプションはヘルプ参照の事、だけど、今回のプログラム書き出すパターンではほぼほぼlrecl=32767の拡張ぐらいしか使わない(逆にこれは設定しておくのが無難)。
細かいながら、putステートメントでの注意点を一つ。このステートメント内には、リテラルか変数ぐらいしか指定できない(変数に付随するフォーマットは設定出来るが)。また、文字列リテラルを2つ並べても、一つの文字列として扱われる(例えば「put 'Hello! World' 'Hello! World' ;」としても、出力では「Hello! WorldHello! World」になる)。

NOTE: 出力ファイル"C:\Users\~~~\AppData\Local\Temp\test.txt" :
      ファイル名=C:\Users\~~~\AppData\Local\Temp\test.txt,
      レコードフォーマット=V,論理レコード長=32767,
      ファイルサイズ (バイト)=0,
      更新日時=2014年12月02日 23時23分18秒,
      作成日時=2014年12月02日 23時04分04秒

NOTE: 1レコードを出力ファイル"C:\Users\~~~\AppData\Local\Temp\test.txt"に書き込みました・
      最小レコード長は25です。
      最大レコード長は25です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.09 秒
      CPU時間            0.09 秒

Hello! World Hello! World

(デフォルトの区切り文字は半角空白)


デバック時には、

data _null_ ;
file log ;
a='Hello! World' ;
put a a ;
run ;

(ログ出力)

data _null_ ;
file log ;
a='Hello! World' ;
put a a ;
run ;

(標準出力)

読み込み。

%include、以上、なのだが、まあ注意点としては、
「実行プログラムとして読み込まれる」のと、「オープンコードとして読み込まれる」という事ぐらいかなあ。
例えばこれがマクロ内で読み込みされてもそのマクロ内で生成されたSASマクロ変数なんかは参照出来ない。*1

options source2 ;
ods listing ;


filename _RSAS "%sysfunc(pathname(WORK))/temp.sas" lrecl=32767 ;
data _null_ ;
	attrib TMPSTR length=$1000. ;
	file _RSAS ;
	TMPSTR = 'proc datasets ' ;
	put TMPSTR ;
	TMPSTR = 'nolist' ;
	put +4 TMPSTR ;
	TMPSTR = ';' ;
	put TMPSTR ;
	TMPSTR = 'contents' ;
	put +4 TMPSTR ;
	TMPSTR = 'data=sashelp.iris' ;
	put +8 TMPSTR ;
	TMPSTR = 'varnum' ;
	put +8 TMPSTR ;
	TMPSTR = ';' ;
	put +4 TMPSTR ;
	TMPSTR = 'quit ;' ;
	put TMPSTR ;
run ;

%include _RSAS ;


ods listing close ;
408
409  options source2 ;
410  ods listing ;
411
412
413  filename _RSAS "%sysfunc(pathname(WORK))/temp.sas" lrecl=32767 ;
414  data _null_ ;
415      attrib TMPSTR length=$1000. ;
416      file _RSAS ;
417      TMPSTR = 'proc datasets ' ;
418      put TMPSTR ;
419      TMPSTR = 'nolist' ;
420      put +4 TMPSTR ;
421      TMPSTR = ';' ;
422      put TMPSTR ;
423      TMPSTR = 'contents' ;
424      put +4 TMPSTR ;
425      TMPSTR = 'data=sashelp.iris' ;
426      put +8 TMPSTR ;
427      TMPSTR = 'varnum' ;
428      put +8 TMPSTR ;
429      TMPSTR = ';' ;
430      put +4 TMPSTR ;
431      TMPSTR = 'quit ;' ;
432      put TMPSTR ;
433  run ;

NOTE: 出力ファイル_RSAS :
      ファイル名=C:\Users\~~~\AppData\Local\Temp\SAS Temporary Files\_TD8700_~~~_\temp.sas,
      レコードフォーマット=V,論理レコード長=32767,
      ファイルサイズ (バイト)=0,
      更新日時=2014年12月03日 00時45分22秒,
      作成日時=2014年12月03日 00時26分12秒

NOTE: 8レコードを出力ファイル_RSASに書き込みました。
      最小レコード長は1です。
      最大レコード長は25です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.03 秒
      CPU時間            0.01 秒


434
435  %include _RSAS ;
NOTE: %INCLUDE (レベル1)ファイル_RSASはファイルC:\Users\~~~\AppData\Local\Temp\SAS Temporary Files\_TD8700_~~~_\temp.sasです。
436 +proc datasets
NOTE: HTML Bodyファイルの書き込み先: sashtml4.htm
437 +    nolist
438 +;
439 +    contents
440 +        data=sashelp.iris
441 +        varnum
442 +    ;
443 +quit ;

NOTE: PROCEDURE DATASETS処理(合計処理時間):
      処理時間           0.89 秒
      CPU時間            0.78 秒


NOTE: %INCLUDE (レベル1)を終了します。
444
445
446  ods listing close ;
                                            SAS システム                            2014年12月 2日

                                        DATASETS プロシジャ

         データセット名      SASHELP.IRIS                 オブザベーション数            150
         メンバータイプ      DATA                         変数の数                      5
         エンジン            V9                           インデックス数                0
         作成日時            2013/06/20 14:38:09          オブザベーションのバッファ長  48
         更新日時            2013/06/20 14:38:09          削除済みオブザベーション数    0
         保護                                             圧縮済み                      NO
         データセットタイプ                               ソート済み                    YES
         ラベル              Fisher's Iris Data (1936)
         データ表現          WINDOWS_64
         エンコード          us-ascii  ASCII (ANSI)


                                      エンジン/ホスト関連情報

 データセットのページサイズ  65536
 データセットのページ数      1
 データページの先頭          1
 ページごとの最大OBS数       1361
 先頭ページのOBS数           150
 データセットの修復数        0
 ExtendObsCounter            YES
 ファイル名                  C:\Program Files\SASHome\SASFoundation\9.4\core\sashelp\iris.sas7bdat
 作成したリリース            9.0401B0
 作成したホスト              X64_7PRO


                                            作成順の変数

                      #    変数           タイプ    長さ    ラベル

                      1    Species        文字        10    Iris Species
                      2    SepalLength    数値         8    Sepal Length (mm)
                      3    SepalWidth     数値         8    Sepal Width (mm)
                      4    PetalLength    数値         8    Petal Length (mm)
                      5    PetalWidth     数値         8    Petal Width (mm)


                                            ソート情報

                                      ソート順        Species
                                      バリデート済み  YES
                                      文字セット      ANSI


補足説明。
「options source2」は、読み込んだプログラムソースをログに出力するシステムオプション。
dataステップ内でのfileステートメントに一気にpathも含めてファイルを設定出来るが、どうせ後で読み込むのでfilenameステートメントで設定しておいた。
putステートメントの後に、+4などと書くことで適当にインデント設定して書き出し出来るが、ほぼ趣味である。
また、filenameステートメントで、ファイルのpathを記載する代わりに「TEMP」というキーワードでWORKライブラリ内に一時書き出しファイルを作成する事が出来る(#LNxxxxなどという名前でファイルの名前も自動設定される)が、ここでは敢えてしていない(しない方が便利な事もある)。

実際に使う場面。

無論、このサンプルは、単に「出来る!」というものでしかないが、dictionaryテーブルやcontentsプロシージャのデータセット出力と組み合わせる事で出来る事は増える。

*1:ただ、SASマクロ変数は、他の言語での、スコープがある変数と同様に扱ってはいけない、基本的には古い言語と同じくグローバル領域の変数になっちゃうので、元からSASマクロ変数に頼るプログラムにするのはオススメしない)(じゃあどうするの?というと、「WORKライブラリ配下に情報伝える為のデータセットを置いておく」というやり方でよく対応する。