2017年11月16日木曜日

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




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

  Y = index(X,"SAS");



INDEX関数を使って、変数X に "SAS" という文字が何バイト目にあるかを取得しています。
FIND関数でも同じ構文で書けます。

  Y = find(X,"SAS");





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


たとえば、
変数X から大文字・小文字区別せずに "SAS" という文字が何バイト目にあるかを取得したいとします。

この場合、INDEX関数では、UPCASE関数も使って以下のように書く必要があります。

  Y =index(upcase(X),"SAS");




しかし、FIND関数では以下のように第3引数に "i" を指定すると大文字・小文字区別せずに検索してくれるので、非常にシンプルな文になります。

  Y = find(X,"SAS","i");




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

FIND関数の詳細はまたいずれ記事書こうかと思ってます。



注意点
日本語などの文字を含む場合は、以下記事の通りうまく動作しないのでご注意ください。
関数の落とし穴:5C問題

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.

   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;




2017年11月2日木曜日

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ビューのメリット


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

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


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

proc freq data=MYLIB.MYVIEW2;
   table C;
run;


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

なぜなら、以下のように

proc freq data=MYLIB.MYVIEW2;
   table C;
run;


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



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



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


次回は「落とし穴編

2017年10月31日火曜日

現在のオプションの設定値を確認する【PROC OPTIONS】



OPTIONSプロシジャをつかうと、特定のオプションの

・設定方法
・現在の設定値

をログに表示することが出来ます。


構文

   PROC OPTIONS   OPTION=確認したいオプション名   表示内容;
   RUN;


表示内容は以下に示す通り、DEFINE, LONG, SHORT などがあります。



DEFINE … オプションの詳細な説明と現在の設定値を表示
proc options option=msglevel define;
run;




LONG … オプションの簡単な説明と現在の設定値を表示
proc options option=msglevel long;
run;



SHORT … オプションの現在の設定値を表示
proc options option=msglevel short;
run;



カッコで囲んで複数のオプションを指定することもできます。
proc options option=(msglevel center) long;
run;