"v_mt" with SAS


BBS「SAS板」 デル株式会社

順次訂正中。リンクフリーです。
SAS言語のリファレンスの積もりで作っている(メモ帳代わりに書くが「リファレンス」を名乗らせてもらっている)。

前の職場に来て以来SASを使うことが多くなった。
SASについての本は多数出版されているものの、必ずしも必要としていることが書かれてないので(特にMS絡み)、まとめてみる。
自分の理解をまとめているので間違っている箇所があるかもしれない。間違いがあったら「SAS板」に書いてもらえれば幸いだ。

内容はデータハンドリング面に特化している(これすら怪しい。現状BASEしか扱ってない)。

初級程度の知識を持っていれば理解できると思う。全く知識のない人向けには作っていない。
バージョンはV8以上を想定しているが、当初僕がV6とV8を併用して使っていたこともあり、バージョンによっては冗長なPGMが含まれている。

「SAS Certification Base Programming」を取得したい場合は、
「SAS Certification Prep Guide Base Programming」がSAS Publishingから出ているので、そっちを読んだ方がいい。
この本は英語だが、試験自体も英語である(070123現在、for9だけになった)。
(左下のamazonは参考までに載せてみた。ちなみにこの試験は受験料が日本だと2万近くかかる)

Advancedのテキストは、Amazonを見る限り2007年末に出版されたようだ(テストには最適化の内容が含まれていた)。

ページが重いが、ご勘弁願いたい。なお、索引の順番は080222現在、整理し切れていない。




SASの正式名称 PG作成で分からないことがあったら
PGのフロー1 PGのフロー2
MS-Excelへの掃きだし MS-Excelからの取り込み MS-Excelの罫線(途中だけど…)
MS-Excelのセル結合(仮?)
MS-ACCESSからの取り込み SQL-SERVERからの取り込み
フォーマットの基本 フォーマットの注意 フォーマット内容の確認
フォーマットをSASDSから取り込む
SASデータセットの転置 コンペア ライブラリ内のデータセットを全削除
変数の並び順 ソート応用編
データセットオプション
SUBSTR関数 COMPRESS関数(改行コードの詰め方) SCAN関数
TRANWRD関数
文字型の数字を数値型に変える
マクロの書き方 マクロの内容をログに出す マクロ内で繰り返す
マクロ変数(1) マクロ変数(2)・CALL SYMPUT文 マクロ変数(3)・%GLOBAL文
ファイルの自動起動 EXCELの自動起動 フォルダの作成、削除
ログを他のファイルに出力
ライセンスの確認 ファイルの拡張子
SQL文を書く
他のSASプログラムを呼び出して実行する
出力にヘッダー・フッターを付ける
その他

SASの正式名称
「Statistical Analysis System」、略して「SAS」。

「Southern All Stars」、「Scandibavian Airline Systems」や、
「Special Air Service」、「Sleep Apnea Syndrome」だと思った人は、
きっと来るHPが間違っている。

ページの先頭へ

PG作成で分からないことがあったら
Googleで探すか、以下を見ると分かるかもしれない。
SAS Technical Supportの中の、「Base SAS」などを参照する。
「Base SAS」の内容は参考になるので、一度位は見ておくといいと思う。

Versionが6と8では、付けられる変数名長、テーブル名長に違いがある。
v6⇒「8」、v8⇒「32」(ラベル名は256らしい)

他にも違いがある。↓例えば、こんなものだ(マニュアルに載ってた代表的なものを書き出す)。
ライブラリの参照名(libname 「」 "〜"の「」部分)⇒8
配列名⇒32
関数名⇒16
マクロ名、マクロ変数名⇒32

SASのマニュアルを見れば全て載っている。


また、全て英語だが…ここか、ここはかなり詳細に書かれている。
これの日本語版があるとよいのだが。


なお、オススメの日本語の書籍は以下の2冊。
「SASによるデータ解析入門」
「事例とSASで学ぶデータ解析」

生物統計のPGMなら「実用SAS生物統計ハンドブック」。

生物統計そのものなら、
「多変量解析による臨床研究」
「生存時間解析」

費えを気にしないのなら、SAS社のトレーニングを受けてみると良い。

(080222追記)
↑日本語に限定してこれらの書籍を挙げてはみたものの、
ローカルSASでのデータハンドリングに関しては、
英語を読むのに抵抗が無いのならばSAS社の資格対策本を購入した方が良いと思われる。

ページの先頭へ

PGのフロー1
何でそうなっているかは仕様だから、としか現状では分からない。

1・サブミット

2・プログラム・スタックにPGを蓄積

3・マクロプログラム部分を先に実行

4・コンパイル

5・実行

例えば、IF〜THEN文などを使った条件分岐でマクロを実行しようとしても、
マクロ部分が先に実行されてしまって条件分岐にならない、
といったことが起こりうる。
(注:2003年8月のSASユーザ会で、「CALL EXECUTE」文の説明の際に前提条件的に聴いた)

(080222追記)
マクロ部分というのは、例えば%LET文のことである。

ページの先頭へ

PGのフロー2
SASは1レコードずつ取り込んでいく特徴がある。
例えば↓データセット「SAMPLE01」と2つのPGがあったとしよう。

TEST01
行1 X1
行2 X2
行3 X3
例1: DATA WK01_1; TEST02=TEST01; SET SAMPLE01; RUN; 例1の実行結果:
TEST01 TEST02
行1 X1
行2 X2 X1
行3 X3 X2
例2: DATA WK01_2; SET SAMPLE01; TEST02=TEST01; RUN; 例2の実行結果:
TEST01 TEST02
行1 X1 X1
行2 X2 X2
行3 X3 X3
例2が通常の流れ。 例1のPGで何故こうなるかについて。 0.行1の処理開始。 1.データをセットする前に命令「TEST02=TEST01」を出しているので、行1にブランクがくる。 2.「TEST02」に値「X1」がセットされる。 3.行2の処理に移る。 4.「X1」がセットされており、命令「TEST02=TEST01」が出されると、行2に「X1」がセットされる。 5.「TEST02」に値「X2」がセットされる。 6.行3の処理に移る。 … (以下略) … ま、慣れてしまえばどうってことはない。 (080222追記) ↑これはSASが1オブザベーション毎に処理していくためである。
ページの先頭へ

MS-Excelへの掃きだし
filename X dde "excel | [(Excelファイル名)](Excelシート名)!(出力範囲)" notab;
data _null_;
	set (SASデータセット名);
	file X;
	T='09'X;
	put (変数X1) T (変数X2) T … (変数Xn);
run;

「data _null_」は、SASデータセットを持たないことを意味する。

「'09'X」は「TAB」キーを意味する(WINDOWSに限る。UNIXだと違ったはず、確か'05'X)。
もし、改行をしたければPUT文を続けて書くといい。
例えば、「PUT 変数X1 T 変数X2; PUT 変数Y1 T 変数Y2;」とした場合、以下のようになる。

列1 列2
行1 X1 X2
行2 Y1 Y2
行3 X1 X2
行4 Y1 Y2
(注1)上記のPGをV6で掃き出した時、語尾or語頭に空白が入ることがありますが、 下記の対策によって改善されます。 1.「T」の前後に「+(-1)」をつける 2.(改善とは言えないが)「T」をつけないようにPGを書く。 また、V8ではFILEステートメントに「DSD DLM='09'X」を設定することで改善されます。 この場合、PUT文でTを設定する必要はありません。SASのTechnical Supportでカバーされていますので、
こちらを参照してください。(write '03/10/19) (注2)掃き出すデータが多い時には、notabの前に「lrecl=50000」などと入れてみるとうまくいくかもしれない (値は適宜変える)。 当然のことだが、 Excelにセルを1つ1つ指定して何回も出力するよりは、 ある程度の範囲にまとめて1回で出力した方が早い。 (080225追記) SASのトレーニングでExcelへのレポート出力が新設されるようだ。 ソース元 ページの先頭へ

MS-Excelからの取り込み
filename X dde "excel | [(Excelファイル名)](Excelシート名)!(出力範囲)";
	data (SASデータセット名);
	infile X notab dlm='09'x dsd missover;
	informat (変数X1) (変数長) … (変数Xn) (変数長);
	input  (変数X1) … (変数Xn);
run;

出力範囲の例:「R2C1:R16C4」

もしSAS/ACCESSが使えるならば、ここまでいちいち書かなくてももっと簡単に取り込めるはず。
但し、不安がある場合はimportプロシジャはやめておいた方がいいだろう。

(061106追記)
V9であればlibnameで.xlsファイルを指定してやることでデータをSASDS形式で見ることができる。
コピーもできそうだが、こっちは試していない。

(070117追記)
missoverオプションを指定すると、
該当ファイルのレコードを読み込む際、全ての変数を読み込む前にレコードが終端に来た場合に、
残りの変数を欠損値として扱う。

ページの先頭へ

MS-Excelの罫線
詳細は未だ分からないが、少なくとも罫線が書けるらしいことは分かった。
以下では指定した範囲を全て罫線で囲んでいる。

filename sys dde "EXCEL | SYSTEM";
DATA _NULL_ ;
	FILE sys ;
	put '[select(%bquote("R2C1:R16C4"))]';
	put '[border(,1,1,1,1)]';
	*↑この意味はput '[border(1)]'と同じか;
	*border(x)は、そのセルの外全体を囲むらしい;
	*border(,x1,x2,x3,x4)の値を変えると、上下左右の線の形状が変わる;
	*どれが上下左右に当たるのかは不明;
RUN;

出力範囲の例として「R2C1:R16C4」を挙げたが、適当に変えれば良い。
borderのパラメータをいじれば詳細が分かりそうだが、指定できるパラメータは少なくとも20近くはあるらしく、
それぞれがどうなっているのか把握できていないのが現状だ。
とはいえ、put文の内容などはVBAが理解できれば問題がなくできるものと思われる(2004/04/29)

頭についているように、「filename sys dde "EXCEL | SYSTEM";」でないといけない。
「filename sys dde "excel | [(Excelファイル名)](Excelシート名)!(出力範囲)";」のように、
ダブルクォーテーション内で範囲を指定することはできなかった。
エラーになるので気をつけよう。

(070123追記)
H=Rさんから同部分に関してのカキコがBBSにありましたので、
参考にしていただけると便利だと思われます。
BBS(本当は直リンクしたかったが、DIONのBBSだからかだめだった)

ページの先頭へ

MS-Excelのセル結合(仮?)
セルの結合は↓これでいけるらしい。
無責任だが…大分昔なので、selectの中の「%bquote()」が必要だったかどうか定かでない。
よってタイトルは(仮)。

例1:
filename sys dde "EXCEL | SYSTEM";
DATA _NULL_ ;
	FILE sys ;
	put '[select(%bquote("R2C1:R16C4"))]';
*	put '[border(,,,,,,,1)]';
	put '[alignment(,,,,,,,1)]';
RUN;

出力範囲の例として「R2C1:R16C4」を挙げたが、適当に変える。(2005/04/03)

例2:
filename X dde 'EXCEL|system';
data _null_;
    file X;
line=compress('[select("r1c1:r3c3")]');
put '[workbook.activate("Sheet1")]';
*↑これを指定しないと、その時開いているシートに対して結合が行われる。;
put line;
put '[alignment(,,,,,,,1)]';
run;

また、↓こういうのもいけるらしい。
put '[select("R[0]C[1]:R[1]C[1]")]';

[]内で相対セルを指定してやるんだそうな。
相対セルの場合は、最初にセルを指定してやらないと、
その時に指定しているセルから見てしまうので、初期設定が必須。

(以下050910追記)
「%bquote()」はそのままでも使えることが分かったが、
「borderじゃなくてalignmentだよ〜」という別のミスの指摘を受けたため、
タイトルを(仮)から(仮?)に変更した。

少しはましになっただろうか。Kさん。

(061106追記)
「%bquote」はORやANDのような、SASが特別に認識する語句の場合でも
マスクするためのマクロ関数らしい。となると、この場合ではいらないか。

ページの先頭へ

MS-ACCESSからの取り込み
proc sql;
connect to odbc 
(noprompt="driver=microsoft access driver (*.mdb);dbq=(mdbファイルのパス)");

create table (SASデータセット名) as 
select * from connection to odbc 
(select * from "(mdbファイルのテーブル名)");

disconnect from odbc;
quit;

「proc sql」文で書いている内容はSQLという言語に従っているので、SQLが分かるなら応用が利くはずだ。

(061106追記)
V9であればlibnameで.xlsファイルを指定してやることでExcelファイルをSASDS形式で見ることができた。
多分mdbでもいける(080109追記、取り込むためのエンジンが必要っぽいが…)。

ページの先頭へ

SQL SERVERからの取り込み
proc sql;
connect to odbc(noprompt="dbq=(データベースのパス);" || 
"SERVER=(サーバー名);" || 
"uid=(ログイン名);" || 
"pwd=(パスワード);" || 
"DATABASE=(データベース名);" || 
"driver={SQL Server}"); 

create table (SASデータセット名) as 
select * from connection to odbc 
(select * from "(データベースのテーブル名)") ;

disconnect from odbc;
quit;

繰り返しになるが、「proc sql」文で書いている内容はSQLという言語に従っているので、
SQLが分かるなら応用が利くはず。

(051005追記)
「dbq」の部分は無くてもいけた。

ページの先頭へ

フォーマットの基本
FORMATプロシジャを使う。
データセットを保存する場合はLIBNAMEは「LIBRARY」でないと(少なくともv6では)、
うまくいかない(04/04/29追記 v8でもうまくいきませんでした)。
保存ができても、取り込みができないようだ。

(070203追記、以下の作成例では数値型を使っているが、文字型の場合はフォーマット名
 ―この場合cat_eff―の先頭に「$」を付ける必要がある)
(フォーマット作成例)
PROC FORMAT;
	value cat_eff
	0='影響はなかった'
	1='少し影響した'
	2='影響した'
	3='かなり影響した'
	;
RUN;

(フォーマットをあてる例)
data Y;
	set X;
	format code cat_eff. ;
run;

「cat_eff」の後の「.」を忘れるとエラーになる。

(あてたフォーマットを外す例)
data Z;
	set Y;
	format code ;
run;

(080109追記: 作業効率の面から考えると、formatをはずすだけならdatasetsプロシジャを使うべき)

ページの先頭へ

フォーマットの注意
フォーマットをあてた変数をテーブルで見るとフォーマットをあてた後の値で表示されるが、
データを加工しようとするとフォーマットをあてる前の値になっていることが分かる。
SAS内部でデータの持ち方がそのようになっているかららしいが、詳細は分からない。
加工の際は注意が必要。
フォーマットをあてた値を、別の変数で作るには↓こんなのがあった気がする。

(新たに作る変数名)=put((フォーマットをあてる変数名),(フォーマット名).);

(061129追記:最後のピリオドは自分で作成したフォーマットについては必要だが、
SASが予め持っているdollar10.2などの場合は不要である)

(文字型の数字を数値型に変えるに関連の記載あり)

フォーマット名は8文字までで、語尾が数字だとエラーになる。
また、FORMATプロシジャを使うことでデータセットにある変数の順序を設定することができる。
変数の順序を変えるのは、LENGTHでも可能だ。
これによって変数の型を変更することもできるだろう。

また、datasetsプロシジャを使って、あてられたフォーマットを全て外すことも可能らしい。
会社のKさんから聞いた。
↓こんなんらしい。

proc datasets lib=(ライブラリ名);
	modify (データセット名);
	format _all_;
quit;

似たようなもので、↓こんなんもあるらしい。

「_character_」全文字変数
「_numeric_」全数値変数

追伸 Kさんありがとうございます。

(061106追記)
V9ではフォーマットが32文字、インフォーマットが31文字までとなった。
文字フォーマットの場合は先頭に「$」が必須である。

(070107再追記)
フォーマットをライブラリに保存する場合は以下のようにする。
PROC FORMAT LIBRARY=LIBRARY;
 〜
RUN;

ライブラリの指定は「LIBRARY」でないといけない。
SASのデフォルトの設定でフォーマットのカタログを
WORKとLIBRARYからしか読み込まないようにしているためである。
変更するにはOPTIONのFMTSEARCHを使う必要がある。

(080222追記)
WINDOWSとUNIXでは、データセットを移管することができても
フォーマットカタログは移管することができない。

ページの先頭へ

フォーマット内容の確認
フォーマットを「LIBNAME LIBRARY "〜";」に保存したとして、
WINDOWSやらでフォルダをそのままクリックして中身を見ようとしても、
具体的な中身までは見れない。
中身が見たい時は↓で確認できる。

LIBNAME LIBRARY "〜" ACCESS = READONLY ; * "ACCESS = READONLY" は読み取り専用で開く、の意味 ;

PROC FORMAT LIB=LIBRARY FMTLIB;
RUN;

ページの先頭へ

フォーマットをSASデータセットから取り込む
例えば、

PROC FORMAT CNTLIN=SAMPLE;
RUN;

とすれば、WORK.SAMPLEの内容をフォーマットとして取り込むことが出来る。
最低限、
「FMTNAME」(フォーマット名)
「START」(値)
「LABEL」(フォーマットのラベル)
の3変数が必須。

あとは必要に応じて
「END」(「START」と併せて、数値の範囲を書く)
を使う。

逆に、「CNTLOUT」オプションを使うとフォーマットをSASデータセットに変換することが出来るが、
「CNTLOUT」オプションを使って作られたデータセットを良く見ると、
設定できる変数が全部出ている(らしい)ので、興味があれば見てみるといい。

ページの先頭へ

SASデータセットの転置
SASデータセットの指定した変数について転置を行う(転置の意味が分からなければここを見る)。

PROC TRANSPOSE DATA=(データ元) OUT=(出力先);
	 VAR (変数1) (変数2) …;
QUIT;

ちなみにこのまま出力すると、先頭の2列に元の変数の情報が残り、
3列目以降に「COL1」「COL2」…となる。

ページの先頭へ

コンペア
COMPAREプロシジャを使う。
同名の変数をもつデータセットを比較する。

proc compare data = WK01 comp=WK02 allobs;
run;

「allobs」の他「list」などで、出力形式を指定することができる。
キーを使ってコンペアを行う時は、例えば以下のようにするといい。

proc compare data = WK01 comp=WK02 allobs ;
	by id1 id2;
run;

(注)リレーショナルDBからデータセットを作成する場合はレコードの順序が保障されないので
(リレーショナルDBの特性らしい)、オートナンバーなどで順番が保障されていないのであれば、
取り込む際にSORTプロシジャをかけておくか、後者を使う方が現実的だ。

データの内容が変わらなくても、データの属性が違う場合はその旨エラーが表示される。
フォーマットが違うとか、LENGTHが違うとかでもエラーが出るので、親切ではある。

(以下念のため)
コンペアの際、同じデータセット名のものに対してかけたいことがたまにあるが、
ライブラリ名が違えばコンペアは可能なので、わざわざ別名のデータセットを作る必要はない。

言わんとするのは、↓これでもコンペアはできるということだ。
proc compare data = library1.WK01 comp=library2.WK01 allobs;
run;

(050903追記)
proc compare data = 〜 comp=〜 〜;
 var (変数1) (変数2) 〜;
run;

とすれば、その変数しか比較しない。

(070726追記)
proc compare data = (データセットx) comp = (データセットy) ;
	var (変数x1) (変数x2) 〜;
	with (変数y1) (変数y2) 〜;
run;

とすれば、x1とy1、x2とy2、…と比較していく。
データセットで中身は同じなのに、変数名が異なる時に使う。

ページの先頭へ

ライブラリ内のデータセットを全削除
DATASETSプロシジャを使う。2行で済む。

PROC DATASETS LIBRARY=(ライブラリ名) KILL;
RUN;

WORK.のデータセットを削除する際はもっと短くなる。

PROC DATASETS KILL;
RUN;

データセットが多い場合、ちょっと時間をくう。


1つずつの場合は、確か↓これでいける。

PROC DATASETS LIBRARY=(ライブラリ名);
	DELETE 〜;
RUN;

ページの先頭へ

変数の並び順
どうでもいいといえばいいのだが、たまに変数の順番が気に入らなくなることがある。

変数の順番を並べたい時は、少なくとも2つの方法がある。どっちも似たようなもんだが…

例1:
DATA 〜;
 
 SET 〜:
 …
RUN;

↑の例の中で、SET文の前に↓に挙げる「1」か「2」を書く。
1.「FORMAT (変数1) (変数2) (変数3)…」
2.「LENGTH (変数1) (変数1の長さ) (変数2)(変数2の長さ) (変数3)(変数3の長さ)…」

PGのフローを考えれば、何となくそうなる理由はつかめる、はず。

以下念のため。
「_COL1」「_COL2」「_COL3」…「_COL8」「_COL9」の順で9変数をもつSASDS「SAMPLE1」について
以下の例2のようなDATAステップを書いたとする。

例2:
DATA SAMPLE2;
 FORMAT _COL9;
 SET SAMPLE1:
RUN;

↑この場合、SAMPLE2の変数の順番は「_COL9」「_COL1」「_COL2」「_COL3」…「_COL8」の順になる。

変数が多い場合は、できるだけ目を合わさない方がいい。(write 05/09/03)

追記。↑これも、変数名--変数名で括れる程度なら良いのだけど、
無い場合、仕様書なりが.xls形式であった場合はコピペした方が早いだろう。

(070422追記)
変数が順に並んでいないからといって、
元となるSASデータセット変数にフォーマットがあてられている場合は、
必要のない限りそのままにしておいた方がよいと考えられる。

ページの先頭へ

ソート応用編
SORTプロシジャを使う、っつーのはMERGEやSET文を多用するSASでは基本中の基本だが…
ちょっと応用に使えそうなものを書いてみる。

基本的には↓こういう書き方だが、

PROC SORT DATA=(〜) OUT=(〜);
	BY (〜);
RUN;

キーが重複するオブザベーションを採らない場合は、↓こういう書き方をする。

PROC SORT DATA=(〜) OUT=(〜) NODUPKEY;
	BY (〜);
RUN;


また、たまにだがデータ量が多すぎてソートに時間をくうことがある。
BY変数が多い時はその傾向が特に強い。
SASのFAQではTAGSORTというオプションを使うことを提示していたが、
俺がやった限りでは(普通にやって45秒位かかったデータで)、かえって時間をくうようになってしまった。

↓こんな風に、SORTSIZEオプションを有効に使った方が時間が短縮できるようだ。

PROC SORT DATA=(〜) OUT=(〜) SORTSIZE=(〜);
	BY (〜);
RUN;

ちなみに↓こういう指定の仕方をする。略語は説明の必要なし。
SORTSIZE=1
SORTSIZE=1K
SORTSIZE=1M


デフォルトはちょっとわからない。
データにもよるのだろうが、普段の仕事で使っている分には50M〜60Mで大分楽になっている。
(061129追記:最適なメモリはデータ量に依存すると思われる)

(070117追記:V9だと重複したオブザベーションを別のデータセットに掃き出すオプションがある)

(070222追記:
PROC SORT 〜 ;
	BY (〜);
	WHERE (〜);
RUN;

 のようにWHEREステートメントも使えるので、次に出るデータセットオプションと組み合わせて使えば
 余分なDATAステップを省くことが可能になる。PGの高速化につながるだろう)

(080120追記:ソートで特定の変数を多用する場合はインデックスの使用を考慮すべき)

ページの先頭へ

データセットオプション
「データセットオプション」なるものがあって、
「KEEP」「DROP」「WHERE」「SORT」「IN」「RENAME」などがある。
ぱっと見たところ、俺が他に使いそうなものは「WHEREUP」位だ。
マニュアルを見ると分かるが、山ほどある。

例えば、KEEPのデータセットオプションを使うと↓こういうことになる。

DATA ... ;
	SET ... (KEEP=X1);
	…
RUN;

WHEREだと↓こうだ。

DATA ... ;
	SET ... (WHERE=(変数1=条件1));
	…
RUN;


「KEEP」「DROP」など、書式が変わるだけであって、命令の概念が変わるわけではないので、
使い方さえ慣れれば、PGを簡潔にできるし、メモリも時間も食わずに済むはず。
例えば、「FREQ」プロシジャで「〜OUT=DAT1(DROP=COUNT)」などできると思う。

INのデータセットオプションは、マッチマージで多用することが多い。

DATA ... ;
	MERGE (データセット1)(IN=X) (データセット2) ;
		BY ... ;
	IF X;
	…
RUN;

こうすると、できあがったデータセットには、
データセット1のキーを含むオブザベーションしか残らなくなる。


他には「PW」でパスワードを設定するか、「ALTER」でパスワードを自動入力するのと、
「GENNUM」「GENMAX」でデータセットの世代管理をする位かな。

「FIRSTOBS」で、指定したオブザベーションから読み込むのと、
「OBS」で指定したオブザベーション数しか読み込まない、という機能もあるようだが、
マッチマージを使う際に厄介なことになる。

「COMPRESS」による圧縮機能もある。
(COMPRESS関数(改行コードの詰め方)の追記に圧縮機能について記載あり)

ページの先頭へ

SUBSTR関数
SUBSTR(X1,X2,X3)

「X1」を対象にし、左から「X2」番目の文字から「X3」文字分取り出す。

例:SUBSTR('123456',2,3)

「123456」の左から2番目の文字(つまり2)から、3文字分、
つまり、「234」が「SUBSTR('123456',2,3)」に対応する。

ページの先頭へ

COMPRESS関数(改行コードの詰め方)
COMPRESS(X1,X2)

「X1」を対象に、「X2」が含まれていれば、「X2」を除いて出力する。
スペースを除く時に多用している。

例1:COMPRESS('123454321','2')

「123454321」から「2」を除き、「1345431」となる。

その他、こんなのもある。
↓のようなSASデータセット「WK01」があったとする。
ID COL1(文字列とする)
行1 1 Y1
行2 2 Y22
この時、以下のPGを実行すると、下記のSASデータセットが作成される。 DATA WK02; SET WK01; IF LENGTH(COL1)=2 THEN COL2=COMPRESS("00" || COL1); IF LENGTH(COL1)=3 THEN COL2=COMPRESS("0" || COL1); RUN;
ID COL1 COL2
行1 1 Y1 00Y1
行2 2 Y22 0Y22
ACCESSやらEXCELからSASに取り込んだ時に、 改行コード(ALT+ENTER)が入り込んでいるケースがある。 一般的に改行コードといえば、次の2つのいずれかをいうらしい。 '0A'X '0D'X アプリケーションでどちらが用いられているのかはっきりしないので、 両方取り除いてしまえばひとまず改行コードは取り除ける。 だから、改行コードが入っているフィールド(以下ではFIELD1)について 改行コードを取り除くには、↓こうするといい。 COMPRESS(COMPRESS(FIELD,"'0A'"X),"'0D'"X) COMPRESS(FIELD1,'0A'X || '0D'X ) 追伸 Kさんありがとう (070119追記) 検索でcompressがあったので、念のため。 v9でcompressオプションを指定できるようになったらしい。 option compress = yes ; で、処理中にデータセットを勝手に圧縮、解凍してくれるようだ。 この処理を行うと容量は食わないが、時間を食う(らしい)。 どの程度の差なのかは試してない。データとPC次第であろう。 少なくとも、データ量が少ない時は使うべきではない。
ページの先頭へ

SCAN関数
SCAN(X1,X2,X3)

「X1」を対象に、「X3」を区切りとし、「X2」番目の語を出力する。
条件分岐で使うことが多い。

例:SCAN('123454321',2,'2')
「123454321」のうち「2」で区切られているものを除くと、
「1」「34543」「1」となり、「34543」が対応する。

仕事上では、この関数はMDY絡みで使うことが多い。

ページの先頭へ

TRANWRD関数
TRANWRD(X1,X2,X3)

「X1」を対象に、「X2」の文字列を、「X3」の文字列に置換する。

例:TRANWRD(_COL1,"MAE","ATO")
変数「_COL1」の内容に「MAE」という文字列が含まれていた場合、「MAE」を「ATO」に置換する。

ページの先頭へ

文字型の数字を数値型に変換する
検索語句に「文字」「数値」「変換」の語句があったため、アイデアとして付け足してみる。

「FIG」という文字型の変数を
数値型(ここでは「NUM」という名前にする)に変えたい場合は、例えば以下でいける。


「NUM=FIG+0」

もしくは、

「NUM=FIG*1」

(070107追記 SASの内部処理で「FIG」が文字型であっても数値型で読み込もうとするためらしい)
FIGの値に数字以外の文字が含まれていた場合は、NUMでは欠損値になる。

逆に、数値型を文字型にする場合(あまりないと思うが)は、
LENGTHで文字型の変数を設定して、

(文字型の変数)=(数値型の変数);

と、ごく単純な式でいけたはずだ。

(061021追記) PUT(変数,フォーマット); で変換するのもありだろう。
(070107修正)↑この追記はここでは間違っていたようだ…
「SAS Certification Prep Guide 〜」によれば(俺が正しく英語を読んだのならば)、
INPUTで文字型を数値型にし、PUTで数値型を文字型にするということであった。
WHEREステートメントの処理が厄介になるし、
その気があるのならPGに明示しておいた方がいいということらしい。

ページの先頭へ

マクロの書き方
同じ作業を続ける場合、マクロを使うと便利なので、書いてみる。

%MACRO (マクロ名)(引数1,引数2,…);
…
%MEND;

↑これで、マクロの基本は完成。マクロ内で引数を使う場合は「&」を語頭につける必要がある。
後は使いたいタイミングで↓を書けばいい。 %(マクロ名); 例1: %MACRO SAMPLE01(NAME1,NAME2); DATA &NAME1; SET &NAME2; RUN; %MEND; … %SAMPLE01(DAT01,DAT02); 例2: %MACRO SAMPLE02; DATA _NULL_; SET WK02; CALL SYMPUT('GLOB1',HENSU1); RUN; %MEND; … %SAMPLE02;
ページの先頭へ

マクロの内容をログに出す
OPTION MPRINT;

逆に、取り消したい場合は「NOMPRINT」とすればいいらしい。
デフォルトはNOMPRINTになっている。

設定がどうなっているのか知りたい場合は、

PROC OPTIONS;
RUN;

をサブミットすれば、だだ長い設定の設定の内容が出力される。
長すぎるので、

PROC OPTIONS OPTION=MPRINT DEFINE;
RUN;

位にした方が分かりやすいだろう。

ページの先頭へ

マクロ内で繰り返す
実際にPGを組む場合はマクロで%DO 〜 %TO … %ENDを使う方が多い。
DO 〜 TO …だと(Array文とか)決まりきったところでしか使えないが、
%DO 〜 %TOだと例えば(実際にあるかどうかはおいておいて)、

WHERE 1 or 2 or 3 or 4 or 5 or 6 ;

↑こんなのを↓こう書けるなど、使える余地が大きい。

WHERE 1
%do i = 2 %to 6 ;
 or &i 
%end ;
;

%do〜%end文はマクロの中でしか書けないので、
使いたいならば形式的にでも全PGをマクロ化した方がいいだろう。


(071011追記)
ものによっては%DO〜%ENDを使って、マクロ変数を使いたい時がある。
マクロ変数mv1,mv2,...,mv5が存在するとして、

例:
%macro test ;
data 〜 ;
  set 〜 ;
  %do i = 1 %to 5 ;
    a&i = &mv&i ;
  %end ;
run ;

…
%mend ;

↑こうするとエラーになる。理由はSASが以下のように解釈するからである。

〜〜〜
「a&i = &mv&i ;」 → 「&mv」って何だ… → 「&mvは存在しません」
〜〜〜

「&mv1,&mv2,...」として解釈させるためには、「a&i = &&mv&i ;」とする。
その際の処理は以下の通り。

〜〜〜
「a&i = &&mv&i ;」

  ↓

「&&mv&i」があるが、「&&」だから直後の「mv」は飛ばして「&i」を先に読み込む。

  ↓

「a1 = &&mv1 ;」 ( 「&&mv1」と「&mv1」は同じ意味なので、「a1 = &mv1 ;」となる)
                     ( 注:「&&mv1」の後に続かなければ「&mv1」と同じ意味になる )
〜〜〜


(071023追記)
「&&mv.&i」だと、「.(ピリオド)」に区切りの意味があるので、うまくいかない。

ページの先頭へ

マクロ変数(1)
%LET (変数名)=(内容);

これだけ。
DATAステップなどでマクロ変数を使う際は、変数名の頭に「&」をつけること。

EXCELやACCESSなどのフルパス指定の際に、時間によってファイル名を変えるなどする際は、
これを多用することになる。

(070124追記)
例えば、
%LET num = 1 ;

data ds1 ;
  number = &num ;
run ;

とすれば、データセットds1の中身は、1obs、1変数で、
変数「number」に1が入っている。


また、以下は考え方の参考までに。

%LET word = January ;

data ds2 ;
  sentence = '&word' ;
run ;

とすると 変数「sentence」には「&word」が入り、

%LET word = January ;

data ds3 ;
  sentence = "&word" ;
run ;

とすると 変数「sentence」には「January」が入る。
何故かシングルクォーテーションとダブルクォーテーションが区別されている。

(080114追記)
マクロのデバッグにはMPRINT, SYMBOLGEN, MLOGICを使うと便利だが、
出力も多くなるので、適当に。

ページの先頭へ

マクロ変数(2)・CALL SYMPUT文
マクロの書き方でちらっと出たが、CALL SYMPUT文を使うと、
DATAステップなどで得られる変数の値をマクロ変数として指定することができる。
例をそのまま引用してみる。

例:
%MACRO SAMPLE02;
DATA _NULL_;
	SET WK02;
	CALL SYMPUT('GLOB1',HENSU1);
RUN;
%MEND;

…

%SAMPLE02;

上記の場合だと、GLOB1がマクロ変数として指定され、中身はHENSU1の値が入っている。


ちなみに、マクロ変数は文字型として扱われる。
また、数値を指定した場合は整数部分しか扱われず、小数部分は切捨てられることになる。
どうしても小数を使いたい場合は、10^(当該数の小数第n位)をかけて整数にしてから、
また同じ数で割るような処理が必要になると考えられる。

(070422追記)
%sysevalfを使えば多少は楽になる気配。詳細はSASのFAQのここを参照。

(071011追記)
SQLプロシジャでinto:を使えばマクロ変数とすることができる。確か↓

例:( _col列の合計をマクロ変数mvarにとる )
proc sql ;
  select sum ( _col ) into : mvar from dataset ;
quit ;

個人的にはnullを考えずに済むため、call symput文よりもメリットが大きい。

ページの先頭へ

マクロ変数(3)・%GLOBAL文
あるマクロの内で使った変数を他のマクロでも使うには、%GLOBALを使う。

例:
%GLOBAL _col1 _col2 _col3;

最近知ったばかりなので「そういうもんなんだ」という他ない(2004/03/27)。

マクロ変数にも「グローバル変数」「ローカル変数」の2つの考え方があるそうで、
そのローカル変数を、大域的グローバルに使う命令だと理解している。

ページの先頭へ

ログを他のファイルに出力する
稀にだが、実行するPGが長すぎて
「ログが一杯になりました」という警告を出してくることがある。
その時に「どうしますか?」というメッセージは出してはくれるのだが、
その対応がイマイチ分からない。

ログがその位長いPGであれば、「PRINTTO」プロシジャを使うといい。

PROC PRINTTO LOG='(ファイルのパス)';
	*「.log」が妥当か;
RUN;

これだけでログが別ファイルに保存されるが、
このプロシジャが実行されて以降はログがSASの画面上に全く出なくなる為、
正常に起動しているかどうか分かりにくい。
使う必要がないならばそれに越したことはない。

LOGができる、っつーことはOUTPUTもできそうだが、こっちは試していない。

(070107追記、PROC PRINTTO;RUN; とすればログがログウィンドウに戻る)
(070124追記、PROC PRINTTO PRINT = 〜でOutputが出力されそうだ)

ページの先頭へ

ファイルの自動起動
OPTION NOXSYNC ;
X '(ファイルのパス)';

ファイルは予め作っておかなければならない。
また、実行する度にDOSウインドウが出てくる。
当初Excelファイルのみかと思っていたが、やってみたら他のファイルでも開けてしまった。

DOSウインドウを開かずとも起動できる方法もあるそうだが、定かでない。

(050910追記)
↑「OPTION NOXSYNC ;」でなく、「OPTION NOXSYNC NOXWAIT;」だとDOSウインドウは
出ないようだ。

(080109追記)
ファイルのパスに半角スペースが含まれているとうまくいかないようなので、
カレントディレクトリを移動してから
半角スペースを含まないようにファイル指定するとうまくいくようだ。

ページの先頭へ

EXCELの自動起動
Xコマンド、というらしいのだが…俺にはまだ分からん。(050910 write)
もっとも、↓これだけだと本当にExcelしか起動しないので殆ど役に立たない。

OPTIONS NOXWAIT NOXSYNC;
X "start excel";


これを使う場合は、EXCELを起動してシートにデータを出力した後で、
ファイル名を指定してセーブする必要があるだろう。

名前をつけて保存し、閉じる命令もある。

(070427追記)
Xコマンドは動作しているOSのコマンドを実行するものだということである。
だから、例えばWindowsのSASでXコマンドを打ったとしてそのPGをUNIXのSASで実行したとしても、
OSで命令の仕方が違うので、うまくいくことはないだろう。

ページの先頭へ

フォルダの作成、削除
これはWINDOWSユーザ向けで、UNIXユーザ向けではないだろう。(051020 write)

OPTIONS NOXWAIT NOXSYNC;
X "md (フォルダを作成するパス)";

削除する場合はmdをrdに変えればいい。
MS-DOSの考え方をそのまま使うならば、フォルダに中身がある時は消せないはず。

ページの先頭へ

ライセンスの確認
2行でできる。見ないとBASE以外に何が入っているか多分、分からない。

PROC SETINIT; 
RUN;

実行例は以下の通り。
こんなのが出る、とだけ分かってくれればいい。

〜〜〜
元のサイト確認データ
サイト名 :
サイト番号 :
期限日付 :
猶予期間 :
警告期間 :
システム生成日付 :
オペレーティングシステム :
プロダクト期限日付 :
---Base Product
---SAS/STAT
---SAS/GRAPH
---SAS/FSP
---SAS/IML
---SAS/ASSIST
---SAS/ACC-ODBC

 猶予期間を過ぎるとwarningメッセージが出る。

ページの先頭へ

ファイルの拡張子
データセットの拡張子はV6は「.sd2」に対し、V8は「.s7dat」と違っている。
WindowsXPなどで拡張子が分からない時は見た目が変わらないので判別できない時がある。

(040809追記)V8では「.sd2」のままでも使うことができるが、
保存すると「.s7dat」になってしまうようだ。
「マイコンピュータ」-「ツール」-「フォルダオプション」で、
拡張子云々の所を表示するように変えてやれば、分かりやすくなるはずだ。

で、拡張子が違っていた場合は、SAS Technical Supportのサイトに
V6からV8(もしくはV8からV6)に変換できるPGが紹介されているので、
そっちを参照して欲しい。

ページの先頭へ

SQL文を書く
SQLプロシジャを使う。↓こんなんだ。

PROC SQL;
 (SQL文)
RUN;

例えば、マッチマージでは、データ構造的に「1対1」「1対多」の結合なら出来るが、
「多対多」の結合はできない。
この場合はSQLプロシジャを使って書くしかないようだ。

そうでなくても、MERGEだと結構時間をくうのにSQLだと早かった、ということもあるので、
時間を短縮したい場合はこっちを試してみるのも一手だと思う。

参考→http://www.techscore.com/tech/sql/

(080109追記)
他にSQLプロシジャの利点としては、DATAステップ⇒SORTプロシジャと
2つの処理を踏まなければならない場合でも、
SQL文だとまとめて1つで処理できることが挙げられる。

ページの先頭へ

他のSASプログラムを呼び出して実行する
「%INC」を使う。

例:
%INC 'SASプログラムのフルパス';

PGの結果はログに出力されても、PGそのものはログには出力されないようだ。

(080109追記)
↑SOURCE2オプションでログに出せたような。

ページの先頭へ

出力にヘッダー・フッターを付ける
アウトプットにヘッダー、フッターをつける。
ヘッダーはTITLE、フッターはFOOTNOTEが対応している。

*1;
TITLE (ヘッダー);
FOOTNOTE (フッター);

PROC MEANS DATA=SASHELP.WORKERS;
	VAR ELECTRIC MASONRY;
	OUTPUT OUT=SAMPLE;
RUN;

複数行にわたって書きたい時は、以下の要領で書けるはず。
TITLE (ヘッダー);
TITLE2 (ヘッダー2行目);

(060908追記、この様な表記で1〜10までいけるらしい。1の次に3を書いた場合、2は1行ブランクになる)


逆に、設定した後で外す場合は以下のようにする。

*2;
TITLE;
FOOTNOTE;

PROC MEANS DATA=SASHELP.WORKERS;
	VAR ELECTRIC MASONRY;
	OUTPUT OUT=SAMPLE;
RUN;

仮にTITLE1,2を設定したとしても、
その後TITLE1を設定すればそれまでのTITLE1,2は書き換えられる。
ヘッダー、フッターを設定したまま、
ヘッダー、フッターを設定していない他のプログラムを回すと、
そのプログラムのアウトプットにヘッダー、フッターが出力されるので、
プログラムを継続して回す場合は、外すという設定が必要。

ページの先頭へ

その他
* 1 ******************************** ;
「_N_」という変数は、データセットのオブザベーション数を意味する。

(071023追記)
↑「データセットのオブザベーション数」って言い方が違うな…
データセット作成に条件を一切つけない場合はこれで合っているはずだが、
条件ではじかれた場合でもカウントされているので、
「オブザベーション処理回数」の方が適切な気がする。
「データセットのオブザベーション数」というと、NOBSになるだろうし。

〜〜〜

また、DATAステップで、

DATA _NULL_;
…
RUN;

とすると、WORK.上にSASデータセットは作成されない。


* 2 ******************************** ;
この前知ったことだが、ボタン操作のみでSASデータセットをCSV形式にすることができる。
V6では「書出し」を使うと簡単にできる。V8ではちょっと変わるはずだが、基本は変わらないはず。
↓
注意点としては、CSVファイルをそのままEXCELで開こうとした時、
1列目の変数名が「ID」となっているとうまく開かないことだ(2000まで97と2000の場合ではSYLKファイルと認識される…)。
詳細は不明だが、「ID」という変数名さえつけなければ特に問題はないだろう。

* 3 ******************************** ;
SAS日付は1582年〜19900年(060908訂正、20000年とのことだ)までしかとらないらしい。
cutoffyearオプションのデフォルトは1960年が基準で、西暦が2桁の場合は1960〜2059年と勝手に決められる。
(070107訂正、「1920〜2019年」である。1960/01/01が日付値の基準になっていたのと混同していた)

これを何とかしたい場合は、
option cutoffyear=〜;
で基準となる年は決められるようだ。

「19900」はともかく、「1582」とはいかにも中途半端だ。
まさか本能寺の変は関係ないだろう…で、何でだろうと思って調べてみた(Wikipediaの「グレゴリオ暦」参照)。

それまで使っていたユリウス暦だと、
うるう年で日がずれていたのでグレゴリオ暦を使うようになった、ということらしい。
しかし1582年に変えたのなら、1583年からにするのが妥当な気がするのだが。

* 4 ******************************** ;
IFステートメントと異なり、WHEREステートメントは
複数行に書いたとしても最後の行に記述した内容しか有効にならないが、
WHEREステートメントが使える場合は、メモリの使用減少や処理速度の向上など、
IFステートメントを使うよりもメリットが大きい。
但しWHERE分は1つのステップで複数書けないとか、そのステップで作成した変数には使えないなど
IFと比べると使用条件に制限がかかる。

* 5 ******************************** ;
ARRAYステートメントの配列が組める場合は、組むと楽になる。

DATA 〜 ;
  ARRAY _COL { 10 } ;
RUN ;

とすると、_COL1 〜 _COL10まで作成される。

また、
DATA 〜 ;
  ARRAY _COL { 3 } _CONE _CTWO _CTHREE ;
 …
RUN ;

とすると、以下の対応関係になる。
  _COL { 1 } = _CONE
  _COL { 2 } = _CTWO
  _COL { 3 } = _CTHREE

配列は多次元で組むことができる。
_TEMPORARY_を付けると、データベクトル上にのみ配列を作成し、データセットには残さない。

* 6 ******************************** ;
(その他、検索語句で出てきた質問から)
文字列を左右逆並びにする関数にはREVERSEがある。
単純にREVERSE()でいけたはず。

(071023追記)
あるデータセットの最終OBSだけ残したいのなら、
endオプションでいける。

data DS2 ;
  set DS1 end = eof ;
  if eof ;
run ;

↑「eof」に意味は無い。
「end」は変えると他の意味になるものもあるので、SASマニュアルを参照。


ページの先頭へ

Topへ戻る

注:「SAS」はSAS Institute Inc.のソフトウェア製品です。

Copyright by "v_mt".