今回はSASを勉強し始めた初心者のとき困った・戸惑ったところを思い出しながら取り上げていきたいと思います。
SASと言うと、毎回SASの説明をするくらい私の周りでは残念ながら認知度が低いので、、、簡単に説明します。
SAS?SASSではなくて?とウェブ系の方から言われるかもしれませんが、全く別物です。
Statistical Analysis System の略で、1960年代に開発され、現在、SAS社が開発・提供している統計解析システムです。
R や SPSS などと比較されることが多い言語です。
高価なシステムのため、容易に学習ができなかったのですが、SAS university Edition や SAS onDemand for Academics が登場し、学習がしやすくなりました。
商用利用でなければ無料で利用できるようになっています。
利用している業界でいうと、医薬系やマーケティングなどでよく使われています。
初心者でもベテランでもやりがちですが、セミコロンを忘れたり、間違えてコロンを付け忘れて、文法エラーを起こしてしまいます。
このときのエラーメッセージは、発生させた場所に応じて違く、メッセージ内容も独特なので、初心者だと何が起きたかわからなくなります。
A = 1 の後にセミコロンがないパターン
75 data TEST; |
76 A = 1 |
77 run; |
___ |
22 |
ERROR 22-322: 構文エラーです。次のいずれかを指定してください: !, !!, &, *, **, +, -, /, <, <=, <>, =, >, ><, |
>=, AND, EQ, GE, GT, IN, LE, LT, MAX, MIN, NE, NG, NL, NOTIN, OR, ^=, |, ||, ~=. |
run の後にセミコロンがないパターン
75 data TEST; |
76 A = hello; |
77 B = 1; |
78 run |
79 |
略 |
83 OPTIONS NONOTES NOSTIMER NOSOURCE NOSYNTAXCHECK; |
_______ |
22 |
76 |
ERROR 22-322: 構文エラーです。次のいずれかを指定してください: ;, CANCEL, PGM. |
ERROR 76-322: 構文エラーです。ステートメントを無視しました。 |
とにかくよくやることなので、エラーが発生したら、まずはこの間違いを疑いましょう。
経験的には、ログに表示される一番上のエラーメッセージの直前が怪しいポイントです。
null(欠損値)の挙動も注意すべき箇所です。
SQLであれば、 where A < 1000 と記載したとき、null は含みません。
しかし、SASの場合、null が含まれてしまいます。
data DT1; |
input A; |
cards; |
1000 |
2000 |
. |
3000 |
run; |
data DT2; |
set DT1; |
if A <= 1000; |
run; |
データセット DT2の結果
変数A |
---|
1 |
. (数値欠損値) |
SASでは null は、マイナス無限のような扱いを受けています。
欠損値を含まないようにするには、 if A < 1000 and A is not null と記載しなければなりません。
また、ソートのときもマイナス無限扱いのため、行の順番を意識しなければならないときは注意が必要です。
SASの文字変数において、空 ” も半角スペースだけの値 ‘ ‘ も等しく欠損値として扱われます。
複数のRMDBを使ったことがあるSQL使いならば戸惑わないかもしれませんが、空 と null は別物だという環境で育った方は注意してください。
数学だとよく見ますが、他のプログラミング言語ではあまり見ない下記のような 1以上3以下といった書き方ができます。(他言語ではPythonくらい?)
data TEST_1; |
set TEST_0; |
if 1 <= A <= 3; |
run; |
この書き方ができることを知らなかったとき、 1<=A だと true か false になるけど、 true / false <= 3 とはいったいなにがしたいんだと混乱した記憶があります。
SASでは様々なデータの結合方法がありますが、最も利用されているのはmergeステートメントによる結合かと思います。
最も利用されている結合方法ですが、SQLをやってからSASをやり始めた方には戸惑うような仕様があります。
なんども同じ風に書くため、なぜ省略して書けないのか、、、と感じることでしょう。
proc sort data=TEST_1; |
by A; |
run; |
proc sort data=TEST_2; |
by A; |
run; |
data TEST_3; |
merge TEST_1 TEST_2; |
by A; |
run; |
SQLだと別名の変数でも結合キーとして使えますが、mergeステートメントだと変数名を揃える必要があります。そのため、面倒ですが、結合前にはいちいち変数名を変更し、揃えないといけません。
SQLだと テーブル名.変数名 として区別しないで同じ変数名を使うと怒られます。
しかし、mergeステートメントでは、「なんのメッセージも表示されず」、後ろに書いたデータセットの値に置き換わります。
data TEST_3; |
merge TEST_1 TEST_2; |
by A; |
run; |
例えば、上記の場合、同じ変数名 B があった場合、データセット TEST_2 の変数 B でデータセットTEST_1 の変数 B の値が上書きされます。
ここで厄介なのは、なんのメッセージも表示してくれないことです。
そのため、そのような事象が起きていることに気づきにくくなります。
この事象を回避するためには、 options msglevel=i; を実行しておく必要があります。
このオプションを実行することで、変数が上書きされるとき、以下のように、ログに上書きされる旨が表示されるようになります。
INFO: 変数B (データセット WORK.TEST_1) はデータセットWORK.TEST_2によって上書きされます。 |
SQLユーザーを悩ませることの大きな一つが n×n結合です。
SASにおけるmergeステートメントでの結合結果は 、SQLでの結合結果と異なるので注意が必要です。
mergeステートメントによる結合とSQLでの結合結果を見比べてみましょう。
テストデータ:データセットTEST_1
A | B |
---|---|
1 | 1 |
2 | 2 |
2 | 3 |
3 | 4 |
3 | 5 |
テストデータ:データセットTEST_2
A | B |
---|---|
1 | 11 |
2 | 12 |
2 | 13 |
3 | 14 |
3 | 15 |
mergeステートメントによるn×n結合とSQLプロシジャによるn×n結合のサンプル
data TEST 3; |
merge |
TEST_l (in=SW1) |
TEST_2 (in=SW2) |
; |
by A; |
if SW1 and SW2 : |
run ; |
proc sql ; |
create table TEST_3_SQL as |
select |
T1.A as A1 |
, T2.A as A2 |
, T1.B as B1 |
, T2.B as B2 |
from |
TEST_1 as T1 |
inner join TEST_2 as T2 |
on T1.A = T2.A |
order by T1.A |
; |
quit ; |
mergeステートメントによる結果
A | B |
---|---|
1 | 11 |
2 | 12 |
2 | 13 |
2 | 14 |
3 | 15 |
3 | 5 |
SQLプロシジャによる結果
A1 | A2 | B1 | B2 |
---|---|---|---|
1 | 1 | 1 | 11 |
2 | 2 | 3 | 14 |
2 | 2 | 2 | 14 |
2 | 2 | 3 | 13 |
2 | 2 | 3 | 12 |
2 | 2 | 2 | 12 |
2 | 2 | 2 | 13 |
3 | 3 | 5 | 15 |
3 | 3 | 4 | 15 |
変数の数はわざと変えているのでそこは注目しなくてよく、注目すべきはレコード数です。大きくレコード数が異なります。
n×n結合をするときには、期待した結果になるか確認しないと悲しいことになります。
ただし、幸運なことに、mergeステートメントで n×n結合が行われたとき、下記のログメッセージが出力されます。
NOTE: MERGEステートメントにBY値を繰り返すデータセットが複数あります |
期待しない結果になっている恐れがあるため、上記メッセージはエラーではありませんが、必ずチェックしておくべきメッセージです。
また、SQLと同じ結果がほしい場合は、上記のように SQLプロシジャを用いるか、SAS9.4から追加された FEDSQLプロシジャを用いるとよいでしょう。
to_number や to_char など分かりやすい名前ではないため、とても覚えにくいです。
最初はどちらがどちらなのかよく間違えます。
put(数値変数, 出力形式) が数値を文字に変換し、 input(文字変数, 入力形式) が文字を数値に変換します。
例えば、マクロ内でエラーが起きたとき、マクロを閉じる %mend が実行されず、以降のソースコードがすべてマクロとみなされ、エラーとなったり、何も実行されないことがあります。
これはコメントや文字列でも似たようなことが発生します。
このとき、下記のコードを実行することで、解消されることがあります。
;*';*";*/;quit;run;%mend; |
解消されない場合は、SASシステムを一度落として再起動してください。
SQLユーザーが、SASを触りはじめて困ったところや戸惑ったところを取り上げてみました。
SASはデータストレージ(HDD/SSD)さえあれば、巨大なデータも簡単に扱ってくれるので便利です。
ただ、他の言語にはないような挙動や仕様に悩まされるため、初心者のときは大変でした。
SASユーザーは人口も少ないので、、、SAS人口を増やすために、SASベテランの方は、本記事を参考に初心者をフォローしていただけると幸いです。