2018年5月24日木曜日

SASユーザー間の情報交換サイト「SAS Japan Discussion」





ついにオープンしました!待ってました!

SAS Japan Discussion
https://communities.sas.com/t5/SAS-Japan-Discussion/bd-p/japanforum-board



https://communities.sas.com」はSAS社のサイトで前から存在してましたが、やり取りは全部英語でした。そこに今回日本語用のページが用意された感じです。

質問や回答したい場合は、アカウントを登録します。

いやー、うれしいです。
あとはちゃんと回答者がついて軌道に乗ってくれるかですね。。私も微力ながら応援していけたらと思います。


2018年5月18日金曜日

ODSグラフを画像ファイルとして保存する際、ファイル名の末尾の開始番号を指定する




ODSグラフを画像ファイルとして保存する方法自体は、以下記事をご覧下さい。
http://sas-boubi.blogspot.jp/2015/09/odssg.html



そして今回は、SAS9.4の多分メンテナンスリリース3辺りから追加になった機能の紹介です。
まず以下のプログラムをご覧ください。

ods graphics  /  reset=index  imagename="SAMPLE"  outputfmt=png ;
ods html close;
ods listing gpath="C:\TEST";

      proc sort data=SASHELP.CLASS OUT=CLASS;
         by AGE;
      run;

      proc sgplot data=CLASS;
         scatter x=HEIGHT y=WEIGHT;
         by AGE;
      run;

ods listing close;
ods graphics  /  reset=all;
ods html;


by変数AGEの値毎に以下6個のグラフが作られます。ここまでは今までの機能です。

SAMPE.png
SAMPE1.png
SAMPE2.png
SAMPE3.png
SAMPE4.png
SAMPE5.png



では次に上のプログラムの青字箇所を以下のように変えて再実行してみます。

reset=index(100)  

すると、ファイル名の末尾の番号が100から始まるようになりました。

SAMPE100.png
SAMPE101.png
SAMPE102.png
SAMPE103.png
SAMPE104.png
SAMPE105.png



SAS9.4の多分メンテナンスリリース3辺りから、ファイル名末尾の開始番号を指定出来るようになりました。

ODS GRAPHICS / RESET = INDEX( ファイル名末尾の開始値 ); 



なので、開始値を1からにしたい場合、

reset=index(1)  

とすれば、SAMPLE1.png ~ SAMPLE6.png と作ることが出来ます。




2018年5月10日木曜日

データセット内の各文字変数について、実データの最大長(LENGTH)を求める




タイトルのような質問をよく頂きます。
製薬業界だと、CDISC対応で文字変数のLENGTHを実データの最大長に合わせたい時に必要になりますね。



求め方は、すでにデータステップ100万回のSASYAMAさんが記事を載せています。
データセット内のすべての文字変数に対して、それぞれに入っているテキストの長さの最大値を取得するハッシュオブジェクト

やりたい事とHASHオブジェクトがマッチしていて素晴らしいプログラムです。私も他の解法を考えてみたくなりました。





サンプルデータ作成

data DT1;
input ONE (TWO THREE FOUR)(:$10.);
cards;
1 aaa cc .
2 bbbb ddd .
3 . ee .
;
  ONE 
  TWO 
 THREE 
 FOUR 
  1   aaa  cc   
  2   bbbb  ddd   
  3 
  ee  

今回はこのDT1が、実データの最大長を取得したい対象のデータセットとします。




※ 本題に入る前に
今回紹介するプログラムでは文字の長さを「LENGTH関数」使って調べてますが注意が必要です(以下参照)

何が言いたいかというと、変数が欠損値の場合、LENGTH関数は「1」を返します。
もし「0」と返してほしいなら、プログラム中の「LENGTH関数」を「LENGTHN関数」に変える必要があります。





方法① 配列とPROC SUMMARYのコンボ

まずは平凡なプログラムから。


※諸注意
以下プログラム中で「_VARNAM」「_VARLEN」「_i」という変数を作っていますが、もし対象のデータセットにもこの3つと同じ名前の変数がある場合、処理がうまくいきません。
その場合はプログラム中の「_VARNAM」「_VARLEN」「_i」を別の変数名(対象のデータセットにはない変数名)に変えてください。


data OUT1;
  set DT1;

  * 文字変数を配列に格納 ;
  array ar(*) _character_;

  * 変数名とlengthを出力 ;
  length _VARNAM $32. _VARLEN 8.;

  do _i = 1 to dim(ar);
    _VARNAM = vname(ar(_i));
    _VARLEN = length(ar(_i));
    output;
  end;

  keep _VARNAM _VARLEN;
run;

* 変数毎の最大lengthを求める ;
proc summary data=OUT1 nway;
  class  _VARNAM;
  output  max(_VARLEN)=_VARLEN  out=OUT2(keep=_VARNAM _VARLEN);
run;

 _VARNAM 
 _VARLEN  
  FOUR    1
  THREE  3
  TWO   4




方法② TRANSPOSEとSQLのコンボ

内部処理的には無駄が多いものの、プログラムとしては単純かつ楽に書けるため私はよくこの方法使ってます。


※諸注意
以下プログラムでは「_TRANUM」という変数を作っていますが、もし対象のデータセットにもこれと同じ名前の変数がある場合、処理がうまくいきません。
その場合はプログラム中の「_TRANUM」を別の変数名(対象のデータセットにはない変数名)に変えてください。

* 後のtranspose用にobs毎の連番を持たせる ;
data OUT1;
  set DT1;
  _TRANUM = _N_;
run;

* 全文字変数を縦に転置 ;
proc transpose data=OUT1 out=OUT2 name=_VAR prefix=COL;
  var       _character_;
  format  _character_;
  by   _TRANUM;
run;

* 変数毎の最大lengthを求める ;
proc sql;
  create table OUT3 as
  select _VAR, max(length(COL1)) as _LEN
  from OUT2
  group by 1;
quit;

 _VAR 
 _LEN  
  FOUR    1
  THREE  3
  TWO   4




方法③ LUAプロシジャ(SAS9.4メンテナンスリリース3から追加になったプロシジャ)

個人的に書いてて一番楽しかったです。
LUAの知識は初心者レベルなので、これが最適解なのか不明、、もっといい書き方やこういう書き方も出来るってのがあったら教えてほしいです。

proc lua;
 submit;

    -- データセットをopen
    local dsid = sas.open("work.dt1")
    local tb = {}

    -- 文字変数名をテーブルに格納
    for var in sas.vars(dsid) do
        if var.type == "C" then
            tb[#tb+1] = {col=var.name, len=0}
        end
    end

    -- OBSを読み込んでいって最大lengthを求める
    while sas.next(dsid) do
        for i in pairs(tb) do
            tb[i].len = sas.max( sas.length(sas.get_value(dsid,tb[i].col)), tb[i].len )
        end
    end

    -- データセットを閉じて&テーブルの中身をデータセットに出力
    sas.close(dsid)
    sas.write_ds(tb,"work.out1")

 endsubmit;
run;


 LEN 
COL
  4   TWO 
  3  THREE 
  1   FOUR



2018年5月8日火曜日

SQLプロシジャのDESCRIBE TABLEを使って、変数定義をログに出力する。




以下構文でデータセットの変数定義をログに出力することが出来ます。


構文

   PROC SQL;
        DESCRIBE TABLE データセット名 ;
   QUIT;





proc sql;
   describe table SASHELP.GAS;
quit;


ログ
NOTE: SQLテーブルSASHELP.GASは次のように作成されました:

create table SASHELP.GAS( label='Nitrogen Oxide Emissions from a Single Cylinder Engine' bufsize=●● )
  (
   Fuel char(8),
   CpRatio num label='Compression Ratio',
   EqRatio num label='Equivalence Ratio',
   NOx num label='Nitrogen Oxide'
  );

●●の部分(bufsize)は環境によって異なります。




あと知らなかったんですが、定義を見たいデータセットが複数ある場合は、以下のようにカンマで区切ってまとめて指定することもできるんですね。

proc sql;
   describe table SASHELP.GAS, SASHELP.FISH;
quit;



以上、まとめて指定できるの知らなかったんで記事かきましたが、薄い記事となってしまいました。