2015年8月18日火曜日

全角スペースを取り除いてくれる関数達




半角スペースを取り除いてくれる関数達」の全角バージョン。

関数仕様
KLEFT
  • 先頭の半角・全角スペースを取り除いて左寄せ。
  • 左寄せした分、半角スペースが末尾に足される
  • 一部の環境では先頭のUnicodeスペースも取り除くようです
KTRIM
  • 末尾の半角・全角スペースを取り除く
  • 一部の環境では末尾のUnicodeスペースも取り除くようです
KCOMPRESS
  • 文字列中の半角・全角スペースを取り除く
KSTRIP
  • 先頭と末尾の半角スペースのみを取り除く
  • 勘違いしやすいので、ここに注意として記載しましたが、全角スペースは取り除けない


注意点

① (2023/1/22確認)
以前のリファレンスには記述されていなかったのですが、一部環境において、
KLEFTでは先頭の、KTRIMでは末尾の「U+000A (改行)」「U+000D (キャリッジリターン)」等の「Unicodeスペース」っていうのも取り除くようです。


② 以下の記事で触れていますが、末尾のスペースを取り除いても、その結果を変数に格納した時点で、結局末尾に半角スペースが入ってしまうんで、そこの取り扱いだけ注意。

2015年8月14日金曜日

DS2プロシジャ入門8:ユーザー定義パッケージ




メソッドをパッケージに入れる事で、その中に入れたメソッドはいつでもどこでも使えるようになります。



構文


パッケージの定義
PROC DS2;

     PACKAGE パッケージ名 / OVERWRITE=YES;

          METHOD メソッド1;
               ~メソッド処理~
          END;

          METHOD メソッド2;
               ~メソッド処理~
          END;
          ・・・
     ENDPACKAGE;

     RUN;
QUIT;




定義したパッケージを使えるようにする

 DCL PACKAGE パッケージ名  インスタンス参照名();


定義したパッケージを使用するには、パッケージのインスタンスってのを作って、そのインスタンスを参照するための変数(インスタンス参照名)を定義します。


パッケージで定義したメソッドを使う

 インスタンス参照名.メソッド();






*** パッケージの作成 ;
proc ds2;
   package MYPACK / overwrite=yes;
       method TEST (double VAR1) returns double;
          return VAR1+10;
       end;
   endpackage;
   run;
quit;

ログ
package mypack がデータセット work.mypack に作成されました。



数値を放り込むと、10足した値を返してくれるTESTというメソッドを作って、
これをMYPACKというパッケージに入れています。


パッケージは通常WORKに作成されるので、SASを終了すると消えてしまいますが、
package ライブラリ名.パッケージ名」 というように書けば、そのライブラリにパッケージが作成され、SASを終了しても残ります。


*** パッケージを使う ;
proc ds2;
   data _NULL_;
       dcl package MYPACK mp();
       method init();
           dcl double OUTVAR;
           OUTVAR = mp.TEST(100);
           put '******' OUTVAR '******';    
       end;
   enddata;
   run;
quit;

ログ
****** 110 ******



dcl package MYPACK mp()」 でパッケージMYPACKをmpというインスタンス参照名で使えるようにし、
mp.TEST(100)」 でパッケージで定義したTESTというメソッドを使っています。

FCMPプロシジャに似てる感じですね。




DS2プロシジャ入門記事

1: 基本構文
: 変数の宣言
3: 変数属性と配列の定義
4: データ型①
8: ユーザー定義パッケージ

2015年8月11日火曜日

Graph Template Language(GTL)入門:PLOTステートメント


作成したいプロットをPLOTステートメントにて設定します。
前回記事で紹介した基本構文の中にPLOTステートメントを入れます。

構文
PROC TEMPLATE ;
   DEFINE STATGRAPH テンプレート名;
         BEGINGRAPH ;
             LAYOUTステートメント  ;
                     PLOTステートメント;
             ENDLAYOUT;
         ENDGRAPH;
   END;
RUN;


PLOTステートメントの一覧  (種類欄に詳細構文のリンクを設定予定)
 種類構文 完成見本

 散布図 

 SCATTERPLOT  X=X軸変数   Y=Y軸変数   /  オプション  ;

 回帰直線 

 REGRESSIONPLOT  X=X軸変数   Y=Y軸変数   /  オプション ;

 時系列プロット 

 SERIESPLOT  X=X軸変数   Y=Y軸変数   /  オプション  ;

 棒グラフ 

 BARCHART  CATEGORY=カテゴリ変数   RESPONSE=応答変数   /  オプション  ;

  (STAT=オプションで棒グラフの統計量を指定可)
  ・RESPONSE=を指定しない場合は以下いずれか指定可
       STAT = FREQ, PCT, PROPORTION (デフォルトはFREQ)

  ・RESPONSE=を指定する場合は以下いずれか指定可
       STAT = SUM, MEAN (デフォルトはSUM)

  (ORIENT=オプションで棒の向きを変更可)
     ORIENT = HORIZONTAL ・・・ 横
     ORIENT = VERTICAL      ・・・ 縦


 階段状のプロット

 STEPPLOT  X=X軸変数   Y=Y軸変数   /  オプション  ;

 BOXPLOT  X=カテゴリ   Y=分析変数   /  オプション  ;


 ヒストグラム

 HISTOGRAM   対象変数   /  オプション  ;


 円グラフ

 PIECHART  CATEGORY=カテゴリ変数   /  オプション  ;

 (LAYOUT REGION /GRIDDED /LATTICE の中でのみ使用可能)


 参照線

 REFERENCELINE  X=数値  /  オプション  ;
   または
 REFERENCELINE  Y=数値  /  オプション  ;

 (他のPLOTステートメントと一緒に使用する)
 (「X=」「Y=」には、設定値を格納した変数を指定しても良い)


 直線(傾き指定

 LINEPARM  X=数値  Y=数値  SLOPE=数値  /  オプション  ;

 (他のPLOTステートメントと一緒に使用する)
 (「X=」「Y=」「SLOPE=」には、設定値を格納した変数を指定しても良い)


 ( ・・・etc  随時更新予定 )



使用例

PLOTステートメントは複数重ねて書くことが出来ます。
以下ではSCATTERPLOT(散布図)とREGRESSIONPLOT(回帰直線)を重ねてプロットしています。

* 適当なデータ作成 ;
data DT1;
label NO="No." HEIGHT="身長(父親)" HEIGHT2="身長(子供)";
input NO HEIGHT HEIGHT2;
cards;
001 165.5 154.5
002 175.8 160.5
003 159.8 150.9
004 180.5 158.2
005 156.8 145.9
;

* 散布図と回帰直線を重ねたグラフテンプレートを作成 ;
proc template ;
  define statgraph MYGRAPH;
      begingraph ;
         entrytitle "親子の身長";
         layout overlay  ;
              scatterplot x=HEIGHT y=HEIGHT2; * 散布図 ;
              regressionplot x=HEIGHT y=HEIGHT2; * 回帰直線 ;
         endlayout;
       endgraph;
  end;
run;

* グラフ作成実行 ;
proc sgrender data=DT1 template=MYGRAPH;
run;




GTL入門記事一覧

2015年8月8日土曜日

「SASユーザー総会2015」に行ってきました。



今年も見たい発表重なりまくりで、発表会場を駆け回る2日間でした。

去年に続き、ブログ「データステップ100万回」と「僕の頁」の中の人も発表されてました。お疲れ様です!


また、数年ぶりに再会する人がいたりして色々楽しかったです。

SASプログラマが集まる場とあって、毎回思わぬ出会いがあり、それだけでも行く価値はあるんじゃないでしょうか。











帰りのアンケートに答えたらSASマニア向けのロゴ入りモバイルケースが貰えました。

使うの勿体ないので、押し入れにコレクションとして大事にしまっておこうと思います。







来年の話もあり、
2016年のユーザー総会は7月下旬で神戸開催予定との事。


あと今年もそうですが、論文形式のものを提出してないと最優秀賞とか優秀賞の対象外になってしまう決まりっぽい。
なので素晴らしい発表なのにパワーポイントしか提出してなくて惜しくも賞を逃した人がいるようです。

来年発表する人は気をつけないとですね。

2015年8月4日火曜日

SASユーザー総会2015で発表の「RWIによる柔軟なレポート作成」の関連記事リンク


ポスター展示にて発表します。
「RWIでカレンダーを作る」っていうタイトルで一発芸的なのにしようと思いましたが、ちょっと肉付けしました。

しょぼい内容ですが、「あ、こんなんでいいんだ」って思って頂いて、自分もなんか発表してみようかなと思って頂ければと思います。

今回の発表とそれに関連するRWIの記事を以下にまとめました。


【RWIの解説】

 ・ レポート作成インターフェイス(RWI)入門1


【発表内容の関連記事】

SUMMARYプロシジャによる楽々転置法



サンプルデータ
data DT1;
input A$ B C$;
cards;
001 10 aa
001 20 bb
001 50 cc
002 . dd
002 100 .
002 200 ee
;






サンプルから、以下のようなデータセットを作りたいとします。
(Aの値毎に各変数値を横に転置しています。)







今回個人的にハマってるSUMMARYプロシジャで色々調べていたら、海外のSAS Global Forumで発表された方法が面白かったので、この方法で転置してみます。
http://support.sas.com/resources/papers/proceedings10/102-2010.pdf


SUMMARYプロシジャのIDGROUPを使った方法
proc sort data=DT1;
   by A;
run;

proc summary data=DT1 ;
   by A;
   output out=OUT1 (drop=_TYPE_ _FREQ_)  idgroup( out[3] (B C)= );
run;


これだけ!
IDGROUPについては以下で紹介しました。
MEANSまたはSUMMARYプロシジャで使えるIDGROUPオプションの紹介

最初、上のリンク記事の例のようにIDGROUPには min(変数) とか max(変数) を書く必要があるのかと思ったらそうでもないんですね!



汎用性をもたせるために、Aのグループ内obs数の最大値を取得して、その数だけ横に並べるようにしたプログラムが以下になります。
proc sql noprint;
   select max(_COUNT) into:_MAX trimmed from
   (select count(*) as _COUNT from DT1 group by A);
quit;

proc sort data=DT1;
   by A;
run;

proc summary data=DT1 ;
   by A;
   output out=OUT2 (drop=_TYPE_ _FREQ_)  idgroup( out[&_MAX] (B C)= );
run;


※ ただし、制限として1つの変数につき横に並べられるのは100個までとなります。
※ 「by A」を「class A」と書くと、合計行が作られてしまうので、必ず「by A」と書いてください。




2015年8月2日日曜日

RWIで迷路を作る



RWIを使って迷路を描画してみます。
(※ SAS9.4以降で動作します)



迷路作成の初期値を設定

%let n    = 37;
%let init = 1234;

n    ・・・ 迷路のマスの数。(必ず奇数を設定する事)
init  ・・・ 迷路の壁と床を決めるために生成する乱数のシード値

初期値を変えるたびに異なる迷路を作ることが出来ます。


迷路の壁と床を乱数で決める

data DT1;

  *** 全マス目分の配列を定義 ;
  array AR(&n, &n) ;

  *** 最初から壁にする部分を2に設定 ;
  do y=1 to &n;
      do x=1 to &n;
         if x in (1,&n) or y in (1,&n) or mod(x*y,2)=1 then AR(x,y)=2;
         else AR(x,y)=1;
      end;
  end;

  *** 乱数を生成して壁の位置を決める ;
  *** 壁の位置 1:上、2:下、3:右、4:左 ;
  call streaminit(&init);
  do y=3 to &n-2 by 2;
  do x=3 to &n-2 by 2;

     *** 1段目は上下左右の壁の乱数を生成 ;
     if y=3 then do;
         * 左のマスが壁だったら上下右のみの壁の乱数を生成 ;
         if AR(x-1,y)=2 then  RAND = ceil(rand('uniform')*3);
         else RAND = ceil(rand('uniform')*4);
     end;

     *** 2段目以降は下左右の壁の乱数を生成 ;
     if y^=3 then do;
         * 左のマスが壁だったら下右のみの壁の乱数を生成 ;
         if AR(x-1,y)=2 then  RAND = ceil(rand('uniform')*2)+1;
         else RAND = ceil(rand('uniform')*3)+1;
     end;

     if RAND=1 then AR(x,y-1)=2; * 上 ;
     if RAND=2 then AR(x,y+1)=2; * 下 ;
     if RAND=3 then AR(x+1,y)=2; * 右 ;
     if RAND=4 then AR(x-1,y)=2; * 左 ;
  end;
  end;
run;


RWIで迷路描画

data _NULL_;
  length VAR1 $5.;
  set DT1;
  array AR(&n, &n);
  dcl odsout ob();
  ob.table_start();

  do y=1 to &n;
      ob.row_start();

      do x=1 to &n;

         *** 壁と床を描画 ;
          VAR1="";
          if (x=2 and y=2) or (x=&n-1 and y=&n-1) then VAR1="☆";
          ob.format_cell(data:VAR1,  style_attr: "background=" || choosec(AR(x,y),"white","black") ||
                                                                  " height=0.4cm  width=0.4cm");
      end;

      ob.row_end();
  end;

  ob.table_end();
run;