海外の方からSAS Plotterのパッケージ化を打診されて、マクロをかなり修正していました。いただいた提案や指摘はとても参考になりました。
その中ですごい初歩的なことを改めて再認識したのでメモしておきます。
SASでリストを扱う
SASではデータセット以外のデータ構造体はありません。(ハッシュオブジェクトもあるか。。)マクロ変数はありますが、これは基本的に文字列を格納するものなので、インデックスを持つデータを扱うことはできません。ですが区切り文字を活用することでマクロ変数を疑似的にリストを作成することができます。
リストというのは一次元の配列のことでRだとc(1,2,3)のように区切り文字を使って定義していますね。他のプログラミング言語にも同様の構造体があります。
SAS Plotterを作成する際にカラーコードをリストとして保持したいことがありまして、以前はproc luaを使っていたのですが、冷静に考えたらデータステップでできることがわかったのでプログラムを修正してproc luaの箇所を削除しました。proc luaはSASマクロ内で定義できないので、開発者的には楽になりました。
リストからデータセットを作成する
マクロ変数をリストとして使う場合は、リストの要素を区切り文字で区切って格納すればOKです。
%let lst = 1,2,3,4,5;
区切り文字はコンマでもブランクでもOKです。
このリストの要素を順番に取り出してデータセットに格納してみます。
data test;
do i = 1 to countw("&lst.");
word = scan("&lst.",i);
output;
end;
keep word;
run;
実行するとデータセットtestはこのようになります。マクロ変数のリストの要素を順番に取り出して変数wordに格納できました。
countwは引数の文字列内の単語数を返します。単語はカンマやブランクに基づいて判定してますので、これでリストの要素数を取得します。
scan関数は引数の文字列からn番目の単語を返します。あとはdoループでscan関数を用いて要素ごとに取り出せば完了です。
word |
1 |
2 |
3 |
4 |
5 |
これでもOKですが、欠損を含むリストの場合だとうまくいきません。試しに以下のリストを定義して同様のプログラムを実行すると以下のようになります。4行目は欠損になるはずですが、欠損のレコードは出力されませんでした。countw関数は区切り文字が連続した箇所は一つの区切り文字としてみなすようです。
%let lst2 = 1, 2, 3, , 5, 6;
data test2;
do i = 1 to countw("&lst2.");
word = scan("&lst2.",i);
output;
end;
keep word;
run;
word |
1 |
2 |
3 |
5 |
6 |
欠損を含むリストを扱いたい場合は、countw関数ではなくcount関数が良いと思います。count関数を指定した文字の個数を数えます。これを用いて区切り文字を数え、scan関数に区切り文字を指定してリストの要素を取り出してみましょう。要素数は区切り文字よりも1多くなりますので、ループ上限はcount関数の返り値に1足しておきます。
data test3;
do i = 1 to count("&lst2.", ",")+1;
word = scan("&lst2.",i, ",");
output;
end;
keep word;
run;
word |
1 |
2 |
3 |
5 |
6 |
欠損値も出力されました。
データセットからリストを作成する
今度はデータセットからレコード順にリストとして出力してみましょう。これはproc sqlを使えば簡単です。intoキーワードを使えばクエリ結果をマクロ変数に格納できるのですが、separated byキーワードを使えば区切り文字を指定できます。
data test4;
do word = "test1", "test2", "test3", "test4" ;
output;
end;
run;
proc sql noprint;
select word into : lst4 separated by " "
from test4;
quit;
%put &lst4.;
マクロ変数をログに出力してみます。以下のように出力できました。
test1 test2 test3 test4
SAS Plotterはパラメータにリストを指定するものが多いので、リストとデータセットを相互変換する処理がたくさん出てきます。通常のデータ処理だとまずやらない処理ですが、マクロ開発をする場合は必須のテクニックだと思います。
まあSASパッケージを開発してる日本人は俺だけなので、役に立つ人は一人だけだけどね。。