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
;

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
;


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ステップ内で完結できるのが最大のメリットだと思います。


📝注意

記事の中で使用している「_N_」は「サブセット化IF」と一緒に使用すると正しく動かなくなりやすいです。