この記事は 『CRESCO Advent Calendar 2018』 25日目(最終日)の記事です。

 

今年も最終日担当、金融ソリューション事業部、兼 3D円グラフ撲滅委員 所属のりーだーです。

今回はSASを勉強し始めた初心者のとき困った・戸惑ったところを思い出しながら取り上げていきたいと思います。

SQL使いから、SASを使いはじめて、もうすぐ5年。
SASユーザー総会で発表したり、経験を積むにつれ、だんだん困っていたところを忘れて、初心者の気持ちも忘れてきたので、備忘録として残そうと思った次第です。

SASとは?

 SASと言うと、毎回SASの説明をするくらい私の周りでは残念ながら認知度が低いので、、、簡単に説明します。

SAS?SASSではなくて?とウェブ系の方から言われるかもしれませんが、全く別物です。
Statistical Analysis System の略で、1960年代に開発され、現在、SAS社が開発・提供している統計解析システムです。
R や SPSS などと比較されることが多い言語です。

高価なシステムのため、容易に学習ができなかったのですが、SAS university Edition や SAS onDemand for Academics が登場し、学習がしやすくなりました。
商用利用でなければ無料で利用できるようになっています。

利用している業界でいうと、医薬系やマーケティングなどでよく使われています。

行末のセミコロンを忘れる / 間違えてコロンを付けてしまう

SASでは基本的に命令文のたびにセミコロンが必要です。
初心者でもベテランでもやりがちですが、セミコロンを忘れたり、間違えてコロンを付け忘れて、文法エラーを起こしてしまいます。
他の言語でもよくあることではありますが、セミコロンが足りませんよ、みたいに分かりやすいエラーメッセージは出てくれません。
このときのエラーメッセージは、発生させた場所に応じて違く、メッセージ内容も独特なので、初心者だと何が起きたかわからなくなります。

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(欠損値)がマイナス無限

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 と記載しなければなりません。

 

また、ソートのときもマイナス無限扱いのため、行の順番を意識しなければならないときは注意が必要です。

空白だけも 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 とはいったいなにがしたいんだと混乱した記憶があります。

mergeステートメントによる結合

SASでは様々なデータの結合方法がありますが、最も利用されているのはmergeステートメントによる結合かと思います。

最も利用されている結合方法ですが、SQLをやってからSASをやり始めた方には戸惑うような仕様があります。

1. mergeでの結合前にソートが必要

なんども同じ風に書くため、なぜ省略して書けないのか、、、と感じることでしょう。

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;

2. 結合キーを揃える必要がある

SQLだと別名の変数でも結合キーとして使えますが、mergeステートメントだと変数名を揃える必要があります。そのため、面倒ですが、結合前にはいちいち変数名を変更し、揃えないといけません。

3. 結合キー以外で、同じ変数がある場合、変数の値が上書きされる

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によって上書きされます。

4. merge ステートメントによる n×n結合の結果が SQLと異なる

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プロシジャを用いるとよいでしょう。

数値から文字の変換は put、文字から数値の変換は input

to_number や to_char など分かりやすい名前ではないため、とても覚えにくいです。
最初はどちらがどちらなのかよく間違えます。

 

put(数値変数, 出力形式)  が数値を文字に変換し、 input(文字変数, 入力形式) が文字を数値に変換します。

エラーになったとき、以降、書き直してもエラーが止まらない/実行されないことがある

例えば、マクロ内でエラーが起きたとき、マクロを閉じる %mend  が実行されず、以降のソースコードがすべてマクロとみなされ、エラーとなったり、何も実行されないことがあります。

 

これはコメントや文字列でも似たようなことが発生します。

このとき、下記のコードを実行することで、解消されることがあります。

;*';*";*/;quit;run;%mend;

解消されない場合は、SASシステムを一度落として再起動してください。

おわりに

SQLユーザーが、SASを触りはじめて困ったところや戸惑ったところを取り上げてみました。

SASはデータストレージ(HDD/SSD)さえあれば、巨大なデータも簡単に扱ってくれるので便利です。
ただ、他の言語にはないような挙動や仕様に悩まされるため、初心者のときは大変でした。

SASユーザーは人口も少ないので、、、SAS人口を増やすために、SASベテランの方は、本記事を参考に初心者をフォローしていただけると幸いです。