2018年1月10日水曜日

PROC FORMAT入門1 : VALUEステートメント




複雑に入り組んだFORMATプロシジャに鋭いメスを入れ、謎や疑問を徹底的に究明したいと思います。
FORMATプロシジャはザックリいうと、

1→男、2→女」 や 「Y→YES、N→NO

のような読み替え・変換を行う事が出来まるプロシジャです。
今回はもっとも基本的な「VALUEステートメント」から解説をしていきます。




ではまず読み替えを行うFORMATの定義から。


FORMATを定義する構文


   PROC FORMAT;

      VALUE フォーマット名
         値1  =  "割り当てる文字1"
         値2  =  "割り当てる文字2"
         ・・・
     ;
   RUN;




proc format;

   value TEST1_
      1 = "MALE"
      2 = "FEMALE"
   ;

   value $TEST2_
      "Y" = "YES"
      "N" = "NO"
   ;
run;

上の例では、以下を定義しています。

・「TEST1_」という FORMAT を定義し、「1→MALE、2→FEMALE」 という読み替えを定義
・「TEST2_」という FORMAT を定義し、「"Y"→"YES"、"N"→"NO"」 という読み替えを定義



ポイント

・FORMAT名の最後は数字じゃダメ(例えば「TEST1」など)
・「"Y"→"YES"」のような文字から文字への読み替えを行うFORMATは、FORMAT名の先頭に「$」をつける(例えば「$TEST」など)





では、先程つくったFORMATを使ってみましょう。


変数にFORMATを割り当てる構文


   FORMAT 変数名 FORMAT名. ;




* Sample data ;
data DT1;
   SEX=1; YN="Y"; output;
   SEX=2; YN="N"; output;
run;
SEX  YN 
 1 Y
 2 N

* FORMATを変数に割り当てる ;
data DT2;
   set DT1;
   format SEX TEST1_. YN TEST2_.;
run;
SEX  YN 
 MALE YES 
 FEMALE  NO

上の例では以下を行なっています。

・変数SEX に FORMAT「TEST1_」を割り当て。
・変数YN に FORMAT「TEST2_」を割り当て。

結果を見ると分かりますが、SEX と YN がそれぞれ対応するFORMAT値に読み替えられていますね。


ポイント

・FORMAT名の後ろにドット「.」を入れる必要があります。
・あくまでも変数にFORMATを割り当てているだけです。
(例えば上の例でSEXの値は「MALE」「FEMALE」に置き換わったように見えますが、それは見た目だけで、内部では元の値「1」「2」のままです。)





変数値をFORMAT値に変換する構文


   新規変数名 = PUT(  変数名 ,  FORMAT名.  );




* Sample data ;
data DT1;
   SEX=1; YN="Y"; output;
   SEX=2; YN="N"; output;
run;
SEX  YN 
 1 Y
 2 N

* FORMAT変換する ;
data DT3;
   set DT1;
   length SEX2 YN2 $20.;
   SEX2 = put( SEX, TEST1_.);
   YN2  = put( YN , $TEST2_.);
run;

SEX 
 YN 
SEX2
 YN2 
 1
 Y 
 MALE
 YES 
 2 
 N
 FEMALE 
 NO

上の例では以下を行なっています。

・変数SEX の値を FORMAT「TEST1_」を使って変換し、結果を変数SEX2 に格納。
・変数YN の値を FORMAT「TEST2_」を使って変換し、結果を変数YN2 に格納。


ポイント
先程の例では見た目だけFORMAT値にしていましたが、今回は値そのものをFORMAT値に変換しています。





PROC FORMAT入門 : 記事一覧

1. VALUEステートメント1
2. VALUEステートメント2
3. INVALUEステートメント
4. PICTUREステートメント1
5. PICTUREステートメント2
6. CNTLIN=オプション
7. CNTLOUT=オプション
8. その他の小ネタ

2017年12月25日月曜日

SASでクリスマスカードを作る。



ぎりぎり間に合った・・・。
SASでクリスマスカード作ってみました。やっつけ仕事みたいにすごいシンプルですが。



























以下がプログラムです。SAS9.4で動作します。

*** 描画用データ作成 *********;
* ツリー部分 ;
data DT1;
input X Y;
cards;
0 30
50 30
50 50
10 50
35 80
20 80
40 100
30 100
45 120
40 120
60 150
80 120
75 120
90 100
80 100
100 80
85 80
110 50
70 50
70 30
125 30
210 30
;
run;

* 星部分 ;
data DT2;
  call streaminit(201712);
  do X2=0 to 210 by 10;
    STAR = rand('uniform')*180;
    output;
  end;
run;

data DT3;
  set DT1;
  set DT2;
run;


*** 描画 ******************;
proc sgplot data=DT3 noautolegend ;
   styleattrs wallcolor=black;
   series x=X y=Y / lineattrs=(thickness=7 color=white);
   scatter x=X2 y=STAR / markerattrs=(symbol=star size=0.6cm color=yellow);
   xaxis min=0 max=210;
   yaxis min=0 max=180;
   inset "Happy Holidays" / position=right textattrs=(color=white size=30cm);
run;



今年はあと1回記事書くか、書かないかって感じですが、あとちょっとで2018年ですね。
お疲れさまでした。

ブログに立ち寄っていただいたり、直接アイディアを頂いたり、みなさん有難う御座いました。
良い年末年始をお過ごしください!!

2017年12月15日金曜日

PROC DS2 の MATRIXパッケージ による行列計算


以前、行列計算をFCMPプロシジャで実現する方法を紹介しました。
FCMPプロシジャと行列計算


同様に、PROC DS2 の MATRIXパッケージというものを使って行列計算を行う事も出来ます。


簡単な例として、以下の連立方程式をMATRIXパッケージによる行列計算で求めてみたいと思います。








行列で解を求める式はこんな感じでしょうか。










proc ds2;
    data OUT1 / overwrite=yes;

    /* 配列を定義 */
    dcl double m1  [3,3];
    dcl double m2  [3,1];
    vararray double out [3,1];

    /* matrixを定義 */
    dcl package matrix _m1;
    dcl package matrix _m2;
    dcl package matrix _inv;
    dcl package matrix _mult;

    method run();

        /* 計算対象の行列をいったん配列に格納 */
        m1 := (1 1 1 2 4 3 5 3 2);
        m2 := (6 19 17);

        /* 配列を行列にSET */
        _m1   = _new_ matrix( m1, 3, 3 );
        _m2   = _new_ matrix( m2, 3, 1 );

        /* 逆行列 と 行列の積 を求める */
        _inv  = _m1.inverse();
        _mult = _inv.mult(_m2);

        /* 結果を配列に格納 */
        _mult.out(out);

    end;

    run;
quit;

データセットOUT1
 OUT1  OUT2  OUT3 
 1 2 3

 x=1, y=2, z=3 という結果が得られました。


いちいち配列をかませないといけないのがちょっと面倒。

2017年12月12日火曜日

HASHオブジェクトで、Key変数毎に最小値・最大値・合計値などを取得する方法


(業務でコピペ利用するための、ほとんど個人的なメモ。)








* Sample Data ;
data DT1;
input NO:$3. SEX:$1. AGE:8.;
cards;
001 F 39
002 M 25
;
run;

NO・・・顧客ID
SEX・・・性別
AGE・・・年齢



data DT2;
input NO:$3. FOOD:$10. BUYDATE:yymmdd10.;
format BUYDATE yymmdd10.;
cards;
001 ORANGE  2017/12/11
001 APPLE   2017/12/13
001 CHERRY  2017/12/10
002 ORANGE  2017/12/15
002 APPLE   2017/12/05
;
run;


NO・・・顧客ID
FOOD・・・購入食品
BUYDATE・・・購入日



上のデータから、顧客ID毎に購入日の最小値を取得したいとします。

data OUT;

    set DT1;

    /* 購入データをHashオブジェクトに格納 */
    if _n_=1 then do;

        length BUYDATE 8.;
        call missing( BUYDATE );

        dcl hash buy( dataset:"DT2", multidata:"y" );
        buy.definekey("NO");
        buy.definedata("BUYDATE");
        buy.definedone();

    end;


    /* Hashオブジェクト内をループして、顧客IDごとの購入日の最小値を取得 */
     rc = buy.find();
     if rc = 0 then MINDATE = BUYDATE;

     do while (rc = 0);
          rc = buy.find_next();
          if rc = 0 then MINDATE = min( MINDATE, BUYDATE );
     end;

     format MINDATE yymmdd10.;
     keep NO SEX AGE MINDATE;
run;





解説



Hashの触りとして以下記事を読むと理解しやすいです。

http://sas-boubi.blogspot.jp/2015/02/hash.html
(SAS忘備録:Hashオブジェクトを使おう)



今回やってる事は、データステップ100万回で解説されてる事なので、以下記事を見て頂くと分かると思います。

http://sas-tumesas.blogspot.jp/2014/07/key-multidata-findnext.html
(データステップ100万回:ハッシュオブジェクトの世界⑧ keyの重複を許容する multidata find_nextメソッド)





かいつまんで解説

  dcl hash buy( dataset:"DT2", multidata:"y" );
  buy.definekey("NO");
  buy.definedata("BUYDATE");
  buy.definedone();

まずはHashオブジェクトの定義から。
データセットDT2をHashオブジェクトに格納し、「multidata:'y'」で、Key値の重複を許しています。
変数NOをKey値、変数BUYDATEをKey値に関連づける変数として定義しています。



  rc = buy.find();
  if rc = 0 then MINDATE = BUYDATE;

findメソッドでHashオブジェクト内をKey値で検索。
該当するKey値があったら、取得したBUYDATEをMINDATEに格納。



  do while (rc = 0);
    rc = buy.find_next();
    if rc = 0 then MINDATE = min( MINDATE, BUYDATE );
  end;

find_nextメソッドで、Hashオブジェクト内の次のレコードを検索。
該当するKey値があったら、取得したBUYDATEをMINDATEと比較し、最小日付をMINDATEに格納。

これをHashオブジェクト内に該当するレコードがなくなるまで、do whileでループしています。




一応今回やってることは、SQLやMEANSとかの方が楽に求められるけど、DATAステップ内で完結できるのが最大のメリットだと思います。



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関数




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

  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問題