2017年11月30日木曜日

【SAS QUIZ】第1問:関数を無効にする。




たいした答え用意してるわけじゃないし、SASマスターにとっては簡単なクイズかも。



問題

以下「???」にプログラムを追加することによって、SUM関数の結果をERRORにさせて、データステップを中止させて下さい。

data TEST;
   ???
   Y = sum(1,2,3);
run;








回答例










私の思いつく正解としては、「ARRAYを使う」です。
以下をご覧ください。

data TEST;
   array sum(1) _temporary_;
   Y = sum(1,2,3);
run;

ログ
NOTE: 配列sumの名前がSAS提供またはユーザー定義関数の名前と同一です。この名前の後ろのかっこは、関数の参照ではなく、
      配列の参照として処理されます。

ERROR: 配列sumに指定した添字の数が多すぎます。
NOTE: エラーが発生したため、このステップの処理を中止しました。



(ARRAYの構文は例なので適当です、やりたい事が出来ていればOK)

ログに出てるメッセージの通り、sumという配列を定義しちゃって、sum関数を認識できないようにしちゃってます。


他に方法ってあるのかな?
思いついた方いたら、コメントお願いします。


2017年11月20日月曜日

共有マクロを作る【MSTORED, SASMSTORE編】



以前、共有マクロについて、以下の記事を書きました。
共有マクロをつくって、プログラミングを効率的にする1


上の記事では、「sasautos=」を使ってマクロプログラムを呼び出す方法を紹介しました。
今回は、「sasmstore=」を使ってコンパイル済みのマクロを呼び出す方法を紹介します。





コンパイルしたマクロを保存する構文

options mstored sasmstore=コンパイルしたマクロを格納するライブラリ;

%macro マクロ名 / store;
  マクロの中身;
%mend;


store」をつけたマクロは、「sasmstore=」に指定したライブラリに保存されます。






libname mymac "C:\MyMacro";
options mstored sasmstore=mymac;

%macro test1 / store;
  data aa;
  run;
%mend;

%macro test2 / store;
  data bb;
  run;
%mend;

C:\MyMacro の中に、コンパイルしたマクロ test1, test2 を格納しています。
(マクロの中身は適当)



ではここで、一旦SASを再起動します。
普通はSASを閉じるとマクロも消えてしまいますが、上のように保存したマクロは mstored・sasmstore=オプションを指定するだけで、また使えるようになります。

libname mymac "C:\MyMacro";
options mstored sasmstore=mymac;

%test1;



2017年11月16日木曜日

【小ネタ】INDEX関数 vs FIND関数




以下のプログラムをご覧ください。

  Y1 = index( X, "SAS");
  Y2 = find( X, "SAS" );

どちらも、変数X に "SAS" という文字が何バイト目にあるかを取得しています。



この2つの関数の違いですが、FIND関数は、INDEX関数の進化版というイメージです。
(FIND関数には第3引数と第4引数があり、これらの引数を使うとちょっと便利)


たとえば、
変数X から大文字・小文字区別せずに "SAS" という文字が何バイト目にあるかを取得したいとします。
この場合、それぞれ以下のような書き方が出来ます。

  Y1 = index( upcase(X), "SAS" );
  Y2 = find( X, "SAS", "i" );

  • INDEX関数では、UPCASE関数で大文字にしてやる必要があります。
  • FIND関数では、第3引数に "i" を指定すると大文字・小文字区別せずに検索してくれるので、非常にシンプルな文になります。



INDEX関数が使われがちだけど、FIND関数の方もレパートリーに加えてあげて下さい。
(「INDEX」は5文字タイピングしないといけないけど、「FIND」は4文字で済んで、ちょっとだけ楽だし)



注意点
日本語などのマルチバイト文字を含む場合は、以下記事の通りうまく動作しないのでご注意ください。
関数の落とし穴


2017年11月14日火曜日

【変数属性の変更】ALTER TABLE vs PROC DATASETS



(※今回は「ALTER TABLE」が主役なので、PROC DATASETS の説明はあんまりしてません。)




PROC DATASETS を使うと、オブザベーションを読み込むことなく直接データセットの変数属性を変えることができますが、PROC SQL の「ALTER TABLE」を使っても同様のことができます。



構文
PROC SQL;
    ALTER TABLE データセット名
    MODIFY  変数属性を変更する文
    ADD       追加する変数と属性を指定
    DROP     削除する変数を指定
    ;
QUIT;




* Sample Data ;
data DT1;
   V1 = 1;
   V2 = 2;
   V3 = 3;
run;

* ALTER TABLE ;
proc sql;
   alter table DT1

   modify  V1 label="aaa" format=yymmdd10.  ,
               V2 label="bbb"

   add      V4  char(10)  label="xxx" ,
              V5  num        label="yyy"  format=yymmdd10.

   drop    V4, V5;
quit;

PROC DATASETS よりシンプルな文になる事があるので、個人的好みはこっちですね。



一応、ADDでやっていることを少し補足しておきます。

   add      V4  char(10)  label="xxx" ,
              V5  num        label="yyy"  format=yymmdd10.

・V4 という文字変数を長さ10で、変数ラベルを"xxx"で追加
・V5 という数値変数で、変数ラベルを"yyy"、formatをyymmdd10で追加




ALTER TABLE でしか出来ない事
「変数追加」や「変数削除」は ALTER TABLE だけ。PROC DATASETS は出来ない(はず)

proc sql;
   alter table DT1
   add  V4 char(10)
   drop V4;
quit;



PROC DATASETS でしか出来ない事
「変数名の変更」は PROC DATASETS だけ。PROC SQL の ALTER TABLE は出来ない(はず)

proc datasets lib=WORK nolist;
   modify DT1;
   rename V2=V10;
run;
quit;


ちなみに、どちらの方法も変数の型(数値・文字)を変更することはできないのであしからず。



得手・不得手がそれぞれありますが、ALTER TABLE でオブザベーションを読み込まずに変数削除できるってのは魅力的です。

是非プログラムのレパートリーに加えてあげて下さい。


2017年11月10日金曜日

【小ネタ】インデックスを使ってSORTプロシジャを省略する


以下をご覧ください。

data CLASS;
  set SASHELP.CLASS;
run;

proc sort data=CLASS;
  by AGE;
run;

proc transpose data=CLASS out=TRA;
  var NAME;
  by AGE;
run;

プログラム自体は適当に書いたやつですが、
変数AGEごとにTRANSPOSEをするために、予めSORTプロシジャでAGE順に並び替えています。




このSORTが煩わしくて、よく以下のように直前のデータステップでついでにインデックスを定義しておいて、SORTプロシジャを省略させてます。

data CLASS (index=(AGE));
  set SASHELP.CLASS;
run;

proc transpose data=CLASS out=TRA;
  var NAME;
  by AGE;
run;

インデックスとして定義した変数は、事前に並び替えせずにBY変数として使える性質を利用しています。



ただし、SORTプロシジャ使って並び替えたデータの方が、その後の処理では内部的に処理効率がよい場合もある点は気に留めておいてください。

2017年11月8日水曜日

DATAステップビューの作成


前回「SQLビューの作成と落とし穴①」を書いたので、似たような機能のDATAステップビューに関しても触れておきます。



以下をご覧ください

data DSVIEW / view=DSVIEW;
   set SASHELP.CLASS;
   keep NAME AGE;
run;

proc print data=DSVIEW noobs;
run;



上で作成した DSVIEW は一見データセットに見えますが、「DATAステップビュー」というものです。
「DATAステップビュー」には、データセットではなく、コンパイルしたプログラムが保存されます。


構文

   DATA  ビュー名 / VIEW = ビュー名;
      ~ プログラム ~
   RUN;



ビューの定義を見たい場合は以下のように書きます。

data view=DSVIEW;
   describe;
run;

ログ
NOTE: DATAステップビューWORK.DSVIEWの定義:

data DSVIEW / view=DSVIEW;
   set SASHELP.CLASS;
   keep NAME AGE;
run;


このビューを開いたり、参照すると、コンパイルしたプログラムが実行されて結果のデータが返ってくる仕組みになっています。




DATAステップビューのメリット


たとえば、以下のようにデータセットDT1から変数Cを作って、その変数にMEANSをかけたいとします。

data STAT1;
   set DT1;
   C = A + B;
run;

proc means data=STAT1;
   var C;
run;


もしDT1の中身が定期的に更新される場合、更新される度に上のプログラムを実行する必要がありますよね。


ですが、以下のようにDT1を参照して変数Cを作るDATAステップビューを1度だけ実行・保存しておけば、、

data MYLIB.STAT1 / view=MYLIB.STAT1;
   set DT1;
   C = A + B;
run;


DT1が更新されても、以下のようにDATAステップビューを参照するMEANSを実行するだけで済みます。

proc means data=MYLIB.STAT1;
   var C;
run;

(ビューを参照するだけで、内部で最新のDT1に対してビューに保存されたデータステップのプログラムが実行され、その結果に対してMEANSが実行されるため。)



またメリットとして、ビューの中身は定義なのでファイルサイズは小さいです。
ただし、プログラム中に何回も同じ中身のビューを参照するような状況では、参照するたびに内部でデータステップのプログラムが実行される事になるので、処理効率が悪くなります。



データステップ100万回でSQLビューとDATAステップビューについて、あと注意点についても書かれてるので、合わせてご参照下さい。

SASのテーブルビュー(view)について

ビューの生成コードを出力するdescribeについて、SQLビューとデータステップビューだと方法が違うから気を付けてって話


2017年11月6日月曜日

SQLビューの作成と落とし穴②



SQLビューの作成と落とし穴①」 の続きです。


落とし穴①

以下をご覧ください。

data DT1;
   A=1; output;
   A=2; output;
run;

libname TEST "C:\Myfiles";

proc sql;
   create view TEST.PITFALL as
   select *
   from DT1;
quit;


TEST.PITFALL というSQLビューを作成しています。特にエラーも出ていません。
続いて、このSQLビューをPROC PRINTで出力してみます。

proc print data=TEST.PITFALL;
run;

ログ
ERROR: ファイルTEST.DT1.DATAは存在しません。
ERROR:  直接的または間接的に参照されたデータセット、ビューの 1 つがオープンできなかったため、SQL ビュー TEST.PITFALL を処理できませんでした


ERRORが出ました。実はSQLビューの性質として、
FROM句のデータセットは、デフォルトでSQLビューと同じライブラリを参照しにいきます。

つまり、SQLビューがTESTライブラリにあるので、「from DT1」は「from TEST.DT1」と置き換えて実行されます。


今回の例では、FROM句に WORK.DT1 を適用したいので、以下のように明示的に指定する必要があります。

proc sql;
  create view TEST.PITFALL as
  select *
  from WORK.DT1;
quit;



落とし穴②

SQLビューは実態がないという点に起因して、想定外の結果になりやすいです。
例えば、システムオプション「FIRSTOBS=」や「OBS=」と併用した場合。


まずは、システムオプションの「FIRSTOBS=」ではなく、データセットオプションの方の「FIRSTOBS=」を使った例。

proc sql;
   create view MYCLASS as
   select *
   from SASHELP.CLASS;
quit;

proc print data=MYCLASS (firstobs=10) noobs;
run;

SQLビューをPROC PRINTで出力していますが、データセットオプションで「firstobs=10」としてるので、SQLビューに対してオブザベーション=10から処理が開始されています。
これは想定通りです。


では次、システムオプションの「FIRSTOBS=」を使った場合。

proc sql;
   create view MYCLASS as
   select *
   from SASHELP.CLASS;
quit;

options firstobs=10;
proc print data=MYCLASS noobs;
run;
options firstobs=1;



「options firstobs=10;」で以降の処理の開始をオブザベーション=10からにしています。

  • まずはPROC PRINTでSQLビューを参照した際にクエリ(SELECT文)が動くので、その時にオブザベーション=10から処理が行われます
  • そのクエリの結果に対してPROC PRINTを実行する際にも、オブザベーション=10から処理が行われます

つまり、FIRSTOBS=システムオプションが2回適用されちゃったわけですね。。

(話それますが、FIRSTOBS=オプションは以下の落とし穴もあって、取り扱い注意)



その他の落とし穴

他の落とし穴についても別記事で書きました。
SQLビューは同じ変数名が複数あってもエラーにならない

2017年11月2日木曜日

SQLビューの作成と落とし穴①


まずはSQLビューについて説明して、次の記事で落とし穴を紹介(記事の最後にリンク貼ってます)。
ミスを起こしやすい仕様があるので落とし穴の記事も必ず参照下さい!


以下をご覧ください

proc sql;
   create view MYVIEW as
   select NAME, AGE
   from SASHELP.CLASS;
quit;

proc print data=MYVIEW noobs;
run;



SQLプロシジャの CREATE VIEW で作成した MYVIEW は一見データセットに見えますが、「SQLビュー」というものです。
「SQLビュー」には、データセットではなく、定義したSELECT文が保存されます。



構文

   PROC SQL;
          CREATE VIEW ビュー名 AS  SELECT文
   QUIT;





ビューの定義を見たい場合は DESCRIBE VIEW を使います。

proc sql;
   describe view MYVIEW;
quit;

ログ
NOTE: SQLビューは WORK.MYVIEW は次のように定義されています :

        select NAME, AGE
          from SASHELP.CLASS;

ビューを開いたり、参照すると、定義したSELECT文が実行されて結果のデータが返ってくる仕組みになっています。




ちなみにビューを削除したい場合は DROP VIEW を使います。

proc sql;
   drop view MYVIEW;
quit;





SQLビューのメリット


便利なのが
たとえば、以下のようにデータセット MYLIB.DT1 を参照している MYLIB.VW というビューを作ったとします。

proc sql;
   create view MYLIB.VW as
   select A+B as C
   from MYLIB.DT1;
quit;



そしてこのビューを使って翌日FREQを実行したいとします。

proc freq data=MYLIB.VW;
   tables C;
run;



しかし翌日、ビューが参照している MYLIB.DT1 に値の変更が発生したとします。
この場合、再度ビューを作り直す必要はありません。

なぜなら、以下のように

proc freq data=MYLIB.VW;
   tables C;
run;


ビューを参照するだけで、内部で最新の MYLIB.DT1 に対してSELECT文が実行され、その結果に対してFREQが実行されるからです。



またメリットとして、ビューの中身は定義なのでファイルサイズは小さいです。
ただし、プログラム中に何回も同じ中身のビューを参照するような状況では、参照するたびに内部でSELECT文が実行される事になるので、処理効率が悪くなります。



分かり辛い上に長くなりましたが、データステップ100万回の方が分かり易く解説されてるので、こちらもご覧ください。
SASのテーブルビュー(view)について


次回は「落とし穴編