2018年6月29日金曜日

【小ネタ】データセットオプション「DROP=」「KEEP=」「RENAME=」の後に変数を指定しなくてもERRORにならない。




以下のように、データセットオプションの「drop=」「keep=」「rename=()」に変数を指定しなくても、ERRORになりません。

data out1;
   set sashelp.class ( drop= keep= rename=() );
run;



変数の指定がないので、これらのデータセットオプションは何もしません。




私はよく以下のような使い方をしています。
%macro test( _drop=, _keep=, _rename= );

    data out1;
      set sashelp.class ( drop=&_drop  keep=&_keep  rename=(&_rename) );
    run;

%mend;




マクロ実行時にdropしたい変数、keepしたい変数、renameしたい変数があれば、それらを指定できるようにし、指定がない場合は何もしない、という分岐を作る事ができます。



たとえば以下を実行すると、drop=, keep=, rename=をしません。

%test( )



以下実行すると、ageをkeepします。

%test( _keep=age )




2018年6月14日木曜日

PROC FORMAT入門8 : CNTLOUT=オプション




「CNTLOUT=オプション」で、FORMATカタログの中身をデータセットに出力することが出来ます。




構文

PROC FORMAT  LIBRARY=ライブラリ.カタログ名  CNTLOUT=出力先データセット ;
       SELECT  出力するフォーマットを列挙;
RUN;


  • 「LIBRARY=ライブラリ名」というようにカタログ名を省略した場合、ライブラリ内の「FORMATS」という名前のカタログがデフォルトで選択されます。
  • SELECTステートメントを省略するとカタログ内の全フォーマットが選択されます。




例

proc format;
   value TEST1_
       1 = "MALE"
       2 = "FEMALE"
   ;
   value $TEST2_
       "Y" = "YES"
       "N" = "NO"
   ;
run;

proc format lib=WORK cntlout=OUT1;
run;



フォーマット「TEST1_」と「TEST2_」が、CNTLOUT=オプションで作成されたデータセットにそれっぽい感じで格納されていることが分かるかと思います。



出来たデータセットの各変数について、以下にざっくり説明しておきます。



CNTLOUT = データセットの各変数の説明

 変数名 型 内容 
 FMTNAME  文字   フォーマット名
 START 文字 開始値
 END 文字 終了値
 LABEL 文字 出力値
 MIN 数値 MIN=オプション値 
 MAX 数値 MAX=オプション値
 DEFAULT
 数値 DEFAULT=オプション値 
 LENGTH  数値 LENGTH=オプション値 。。とリファレンスに書いてありますが、
 そんなオプション存在しないと思われるので詳細不明
 FUZZ   数値 FUZZ=オプション値
 PREFIX   文字 PICTUREフォーマットのPREFIX=オプション値 
 MULT 
 数値
 PICTUREフォーマットのMULTIPLIER=オプション値
 FILL  文字 PICTUREフォーマットのFILL=オプション値
 NOEDIT   数値 PICTUREフォーマットのNOEDITオプションが有効かどうか
 (1=有効 0=無効)
 TYPE 

 文字

 フォーマットの種類
  N = 数値フォーマット
  C = 文字フォーマット
  I = 数値インフォーマット
  J = 文字インフォーマット
  P = PICTUREフォーマット

 SEXCL

 文字

 開始値の取扱い
 ("Y"=開始値を含めない "N"=開始値を含める)

 EEXCL  

 文字

 終了値の取扱い
 ("Y"=終了値を含めない "N"=終了値を含める)

 HLO

 文字

 開始値・終了値の追加情報 (すごく重要な変数)

  O = OTHER

  L = LOW
  H = HIGH
  R = PICTUREフォーマットのROUNDオプション
  ...等。詳細はリファレンスを参照 


 (組み合わせて使用します。例:Low-High…LH)


 DECSEP  文字 PICTUREフォーマットのDECSEP=オプション値
 DIG3SEP 文字 PICTUREフォーマットのDIG3SEP=オプション値
 DATATYPE  文字 PICTUREフォーマットのDATATYPE=オプション値
 LANGUAGE 文字 PICTUREフォーマットのLAUNGUAGE=オプション値






次に色々なフォーマットをCNTLOUT=でデータセットに出力して、どんな感じのデータが出来るか見てみます。

例2

proc format;
   value TEST1_
      1 = "MALE"
      2 = "FEMALE"
   ;
   value $TEST2_
      "Y" = "YES"
      "N" = "NO"
   ;
   value TEST3_
      1 - 3 = "1~3"
      other = "その他"
   ;
   invalue TEST4_
      "MALE"     = 1
      "FEMALE" = 2
   ;
   invalue $TEST5_
      "Y"    = "YES"
      "N"    = "NO"
   ;
   picture TEST6_ (default=3)
     low -< 0 = '00' (prefix="-")
     0 - high = '00'
   ;
   picture TEST7_ (round)
      low - high = '009.00%'
   ;
   picture TEST8_ (default=10)
      low - high = '%Y_%0m_%0d' (datatype=datetime)
   ;
run;


proc format lib=WORK cntlout=OUT1;
run;


結果は以下。画像をクリックすると拡大されます。



2018年6月7日木曜日

特定のシステムオプションをSAS起動時の設定に戻す【GETOPTION関数の使用例】





この前、以下GETOPTION関数の記事を書きましたが、今回は使用例を紹介します。
【GETOPTION関数】システムオプションの設定値を取得したりする。



例えば、PAGESIZEを200に設定したとします。

  options pagesize=200;




後から、このPAGESIZEをSAS起動時の設定に戻したい場合、以下のように書くだけでOK。

  options %sysfunc( getoption(pagesize,startupvalue,keyword) );


%SYSFUNCってなんだって人は以下も参照ください。
データステップ外で関数を使えるようにする「%SYSFUNC」その1



一応ちゃんと設定がSAS起動時の状態に戻ったか確認してみましょう。

  %put NOTE: OPTIONS %sysfunc( getoption(pagesize,keyword) ) に設定されています;

ログ
 NOTE: OPTIONS PAGESIZE=60 に設定されています

はい、私の環境だと PAGESIZE=60 なんで戻ってますね。



2018年6月2日土曜日

【GETOPTION関数】システムオプションの設定値を取得したりする。




GETOPTION関数でシステムオプションの設定値を取得できます。
試しにPAGESIZEオプションの設定値を取得してみます。



オプションの設定値を取得する例

options pagesize=200;

data test;
   length op $20.;
   op = getoption("pagesize");
run;

  op  
  200 

現在のPAGESIZEの設定値「200」を取得できました。
そして、この関数はこれだけじゃないですよ!オプションの取得方法も細かく指定できます。




「オプション名=設定値」の形式で取得する。

  op = getoption("pagesize","keyword");

  op  
  PAGESIZE=200 

PAGESIZEを設定する時は以下の赤文字部分の書式で書きますが、この部分が取得できるというわけです。

options pagesize=200;





SAS起動時の設定値を取得する。

  op = getoption("pagesize","startupvalue");

  op  
  60 





オプションの取得方法を複数指定する。

  op = getoption("pagesize","startupvalue","keyword");

  op  
  PAGESIZE=60 

SAS起動時の設定値をOPTIONSステートメントの書式で取得しています。



一番使えそうなのはこの辺ですかね、他にも色々指定方法があるんで、詳細はマニュアルをご覧くださいね。


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;


  col 
 len 
 TWO  4 
 THREE  3 
 FOUR  1