【SAS】Enterprise Guideを止めるコマンドは存在しない!

該当する人は多くないかもしれませんが、SAS Enterprise Guide(以下EG)での作業が定常化してきて、EGのスケジュールを用いてWindowsのタスクスケジューラに登録してバッチ化しています。(残念ながら、Base SASは現環境にありません)
そうすると、データが存在するかどうかを確認して、存在しなかったらEGを止める必要が出てきました。

EGはコマンドでは止まりません

結論から言うと、EGはコマンドでは止まりません。
同じようなことを考える人はいるようで、"SAS Enterprise Guide vs. Batch"という、タイトルを見ただけで軽く笑いがこみ上げてくる論文を見てみたところ

  • OPTIONS SYNTAXCHECK
  • ENDSAS
  • OPTIONS ERRORABEND
  • ABORT statement
  • %ABORT statement
  • 自動マクロ変数 &SYSERROR

とか、全部試してみたけど、プロセスフローの上流にあるプログラムは止まっても、下流のプログラムは止まらない。
下手すると、下流でサーバに再接続して実行するから有害、とのことでした。

[PDF] EG vs. Batch

どうしてもバッチを止めたい

止める方法は全くないということはありません。

  • SAS Data Integration Studioを買う
  • EG内で一つのプログラムにすべてのコードを記述する(EGをBase SASのように使う)
  • エラー発生フラグをマクロ変数に格納し、EG内の全てのプログラムの先頭にフラグを確認して実行制御するマクロを書く(クエリビルダとか大変)
  • EGがはき出したVBSに小細工をして、条件でスクリプト停止させる

VBSで頑張ることになりそうです。

【SAS】キーが複数存在するテーブルで重複レコードを抽出する

重複レコードの数を数える

キーが複数あるようなテーブルで、重複しているレコードを取り出したいことがある。
そんなときは、まず重複するレコード数を数える。
たとえば、ID1とID2の2つを指定することでレコードが一意になるテーブルで、ID1とID2のセットで重複がいくつあるか数える場合は次のSQLが使える。

PROC SQL;
  CREATE TABLE DUPLICATION_COUNT AS
  SELECT
     ID1
    ,ID2
    ,count(*) as COUNT_of_ROW label = "重複行数カウント"
  FROM
    DATA_IN
  GROUP BY
    ID1, ID2
  HAVING
    count(*) > 1
  ;
QUIT;

このSQLを実行するとID1とID2のセットで重複しているものだけを抽出できる。
ついでに何行重複していたかをCOUNT_of_ROWに格納している。

重複レコードを取り出す

上記のDUPLICATION_COUNTテーブルができていれば、ID1とID2で元テーブル(DATA_IN)とinner joinしてあげれば、重複しているレコードを取り出すことができる。

【SAS】マクロで戸惑ったこと

SASでマクロを書いたときに、戸惑った覚えがあることを残しておきます。
どうでも良いですがSASでマクロを書くと、(書いたこと無いけど)COBOLとかの昔からある言語はこんな感じかなぁと思ってしまいます。

PUT、INPUT関数

これを%SYSFUNCの中で書いたら、怒られてびっくりしました。
マクロの中ではPUTN、INPUTN関数を使わないといけないです。
他にも、%SYSFUNCで使えない関数は、こんなにあります。

  • ALLCOMB
  • ALLPERM
  • DIF
  • DIM
  • HBOUND
  • IORCMSG
  • INPUT
  • LAG
  • LBOUND
  • LEXCOMB
  • LEXCOMBI
  • LEXPERK
  • LEXPERM
  • MISSING
  • PUT
  • RESOLVE
  • SYMGET

SAS(R) 9.2 Macro Language: Reference

INTNX関数

今日から1ヶ月後の日付を計算してマクロ変数に格納したいということがあると思います。
このときにINTNX関数を使うのですが、この関数の第1引数でものすごくはまった記憶があります。
たとえば、今日から1ヶ月先の日付を取得したい場合、マクロ以外では

INTNX('MONTH', TODAY(), 1)

と第1引数にクオテーション付きで時間間隔を指定する引数を与えますが、マクロでは

%SYSFUNC(INTNX(MONTH, %SYSFUNC(TODAY()), 1))

と第1引数にクオテーションは不要となります。
Technical Support:nヶ月先の同日日付を取得する方法(SAS 9.1以降)

【SAS】ROC曲線を描く

PROC LOGISTICのオプションではなくて、予測値と実績値が揃っているデータセットに対し、ROC曲線を描く方法。

ここを参考にしました。
http://www2.sas.com/proceedings/sugi22/POSTERS/PAPER219.PDF

DATAIN = データセット
LOWLIM = データ分割範囲の下限
UPLIM = データ分割範囲の上限
PROB = 予測契約確率
TF = 実績値("0"か"1"の数値)
NINC = 分割数
デフォルトではデータ範囲を20分割してROC曲線を描きます。
PROC GPLOTで図を描いてますが、ROCのデータセットをエクセルにコピーして、エクセルで図を書いた方がそれっぽくなります。

%MACRO ROC(DATAIN, LOWLIM, UPLIM, PROB, TF, NINC = 20);
  OPTION MTRACE MPRINT;
  DATA ROC;
    SET &DATAIN;
    LOWLIM = &LOWLIM;
    UPLIM  = &UPLIM;
    NINC   = &NINC;
      DO I = 1 TO NINC + 1;
        CUTOFF = LOWLIM + (I - 1) * ((UPLIM - LOWLIM) / NINC);
        IF &PROB > CUTOFF THEN TEST = 1;
        ELSE TEST = 0;
        OUTPUT;
      END;
    DROP I;
  RUN;

  PROC SORT;
    BY CUTOFF;
  RUN;

  PROC FREQ;
    BY CUTOFF;
    TABLES TEST * &TF / OUT = PCTS1 OUTPCT NOPRINT;
  RUN;

  DATA TRUEPOS;
    SET PCTS1;
    IF &TF = 1 AND TEST = 1;
      TP_RATE = PCT_COL;
    DROP PCT_COL;
  RUN;

  DATA FALSEPOS;
    SET PCTS1;
    IF &TF = 0 AND TEST = 1;
      FP_RATE = PCT_COL;
    DROP PCT_COL;
  RUN;

  DATA ROC;
    MERGE TRUEPOS FALSEPOS;
    BY CUTOFF;
    IF TP_RATE = . THEN TP_RATE = 0.0;
    IF FP_RATE = . THEN FP_RATE = 0.0;
  RUN;

  PROC GPLOT DATA = ROC;
    PLOT TP_RATE * FP_RATE = CUTOFF;
  RUN;
%MEND;

【SAS】EMのデータをEGで読み込む

EGとEMを同時に使っている環境はわりとあると思う。たぶん。
ただ、EGとEMでデータセットをやりとりしようとすると、なぜかうまくいかないことがある。
そこで、割と無理矢理編み出した感があるデータセットのやりとり手法を忘れないうちに載せておこうと思う。

EG→EMにデータセットを渡す場合

EGとEMが同じSASサーバ上にインストールされている場合は、同じライブラリを見える様にしてあげればEGもEMも中に入っているデータセットを参照できるようになる。
EMでライブラリを指定する場合は、プロジェクトのプロパティにある「プロジェクトの開始コード」にLIBNAMEステートメントを書いてあげればよい。
一番最初に書いたときは、書いたコードを選択して「即時実行」ボタンを押す必要があるが、それ以降はEMプロジェクトを開くときに自動実行されているため、ライブラリは自動で見える様になっている。
あとは、EM側でデータソース作成するときに、先ほど見える様にしたライブラリのデータセットを選んであげれば良い。

EM→EGにデータセットを渡す場合(通常編)

EM上でモデルによるスコアリングをしたデータをEGに渡したいときは、「ユーティリティ」タブにある「データの保存」を使用すれば良い。
データの保存先を先ほど指定したライブラリにすれば、EGからも見える様になる。
EM上ではスコアされたデータを確認するには、モデルノードなどを選択した上で、左のプロパティにある「エクスポート」を押すと、EMが裏で生成しているデータセットの中身を確認することができる。

EM→EGにデータセットを渡す場合(裏技編)

EMはプロジェクトごとにワーク領域を作成して、そこにスコアしたデータなどを全て書き出している。
その領域をEGから強引に読み込みにいくということができる。
以下にその手順を書く。

EM上で書き出したいデータセット名とライブラリ名を把握する

たとえば、「回帰ノード」でスコアされたデータを取り出したいときは、「エクスポートデータ」のTRAINデータが該当する。
この場合、EM上では「EMWS.Reg_TRAIN」と表示されているはずである。
この場合、「EMWS」がライブラリ名で、「Reg_TRAIN」がデータセット名である。

EMがワーク領域としている場所の絶対パスとライブラリ名を把握する

先ほどの「EMWS」はEMが勝手に作成したライブラリ名であるが、実はEM画面の下側にある「ログ」タブを押すとプロジェクトログが確認でき、「EMWS」の絶対パスが確認できる。
ログの中に「ライブラリ参照名EMWSを次のように割り当てました。」という記述があるはずなので、そこを探して欲しい。

EGで、先ほどのライブラリ名と絶対パスをLIBNAMEステートメントで書く

EG上で先ほど確認したライブラリ名と絶対パスをLIBNAMEステートメントで書けば、そこにあるデータセットを確認できてしまう。
なぜか、ライブラリ名はEMで指定されているものと同じで無ければエラーになるので注意。

【SAS】プロンプトを使ってみた

この前、とある人からSASの研修資料片手に、「プロンプトって何?」と聞かれた。
正直、そんなのあったっけ?という感じだったので、ちょっとさわってみたら使い方によっては便利だった。

プロンプトの使い方

(SAS EGのスクリーンショットを撮れる環境では無いのであしからず)
以下は、SAS EG上での操作

  1. クエリビルダや、プログラムを右クリックしてプロパティを開く
  2. 左のカラムに「プロンプト」があるのでクリックして、「プロンプトマネージャ」ボタンを押す
  3. プロンプトマネージャで「追加」を押して新規プロンプトを作る
  4. プロンプトの名前はマクロ変数名と同じようなものなので英語名で付ける
  5. 表示テキストはわかりやすい日本語にしておく
  6. プロンプトが作成できたら、プロンプトマネージャを閉じて、プロンプトを追加する
  7. あとは、プロンプトを使いたい場所(Where句とか)にプロンプトを使ってみる(コードを書いている場合はマクロ変数と同じ扱い)

プロンプトが便利なシーン

プロンプトを使う場面は正直あまり無いと思うが、たとえば、期間をWhere句で指定して、何度も期間を変更しながらTry & Errorでデータを作りたいときなどは、いちいちプログラムを開くより便利かと思った。

【SAS】IFNとIFC

条件によって処理を変えたいとき

普通に考えればif文やcase文を使う。
たとえば、販売金額(sales)10万円以上の場合は、手数料(commission)が2%で10万円未満では手数料が5%みたいな計算をしたいとき、dataステップでは

if sales >= 100000 then commission = sales * 0.02;
else commission = sales * 0.05;

SQLでは

case when (sales >= 100000) then sales * 0.02 else sales * 0.05 end as commission

IFN関数やIFC関数をつかってみる

2つにしか条件分岐しない場合は、IFN関数やIFC関数が使えないか検討して見た方が良い。
先ほどの例をIFN関数で書き直すと

commission = IFN(sales >= 100000, sales * 0.02, sales * 0.05);

となり、結構シンプルに書ける。
IFN関数は条件に合わせて数値を返すときに、IFC関数は文字を返すときに使う。
使い方がシンプルで
IFN(条件, Trueの時に返す, Falseの時に返す, missingの時に返す)
という感じ。missingの時に返すものは無くても良い。