2020年10月1日木曜日

マクロ変数が空であるかを条件にして分岐させる話

マクロ変数が空かそうでないかでプログラムを分岐できませんか?って聞かれました. ちょっと具体的な状況がピンとこないところですが,とりあえず分岐だけはしたのでここに供養します.
最近sasを触らずsuper excel おじさんになっていたので良い気分転換です.

分岐の時にマクロ変数を空白と比較してあげると空かどうかの判定に使えます.以下で言う所の %if &test = %then %do ; ですね. 以下のプログラムでマクロ変数 testが空の時は空とログに出してくれますし,そうじゃない時はからじゃないよってログに出してくれます.
そう言えば近頃のsasでは%ifが平文で使えるので,いちいちマクロを定義して実行って形にしなくて良く見やすいプログラムがかけますね.

option mprint;
%let test=;
%if &test = %then %do ;
data _null_ ;
  putlog "マクロ変数が空です" ;
run ;
%end ;

%if &test ^= %then %do ;
data _null_ ;
  putlog "マクロ変数が空じゃありません" ;
run ;
%end ;
option nomprint ;

実行ログは以下の通りです.マクロ変数が空なことを示すメッセージのみがログに出ています.
460 option mprint;
461 %let test=;
462
463 %if &test = %then %do ;
464 data _null_ ;
465 putlog "マクロ変数が空です" ;
466 run ;
 マクロ変数が空です
NOTE: DATAステートメント処理(合計処理時間): 処理時間 0.00 秒 CPU時間 0.00 秒
467 %end ;
468
469 %if &test ^= %then %do ;
470 data _null_ ;
471 putlog "マクロ変数が空じゃありません" ;
472 run ;
473 %end ;
474
475 option nomprint ;

このマクロ変数の戻り値に半角スペースって昔は通らなかったんですよね…いつか忘れましたが気づいたら通るようになっていました.便利になったもんです. 通らなかった時はマクロ変数をlength関数に入れて,戻り値が0だったらマクロ変数は空とする.みたいに書いていました.

2020年9月1日火曜日

32bit環境で作ったsasのformatを64bit環境にに移送する話

32bit環境で作ったSASのforamtはそのままだと64bit環境では使えません.データセットは互換あるのですが...使えないでは困る場面があるので移行する方法を紹介します.エンコードとかがすごーくややこしそうな局面と思えるのですが,今回は扱いません.

32bit環境で移行用のファイルを作成し,64bit環境で移行用ファイルを解凍します.移行には32bit環境での準備が要るっぽいので,format含めて移行が必要な場合は早めに準備しておいた方が良さそうですね...たまにcptファイルでデータを頂くことがあるので,その場合は以下の新環境で実行するプログラムを実行するとokです

エンコードの話とか怖くて触れたくない,出会いたくもない.ややこしいのが来ないよう日々祈るばかり.マジで勘弁してくれ

/*>>>>>---------- ここから旧環境で実行 ----------<<<<<*/
LIBNAME CAT32 "移行したいsasデータセットとsasのformatファイルのパス";
filename FILE64 '移行用ファイルを保存するパス\移行用ファイル名.cpt';

proc cport lib=CAT32 file=FILE64 ;
run ;

/*>>>>>---------- ここから新環境で実行 ----------<<<<<*/
libname CAT64 "移行後のデータセットを保存したいパス" ;
filename FILE64 "移行用ファイルを保存するパス\移行用ファイル名.cpt" ;

proc cimport lib = CAT64 file = FILE64 ;
run ;

2020年8月1日土曜日

SGPLOTでバタフライプロットを書く話

最近グラフおじさん化していますが世の中のグラフの種類の多さにたまげる毎日です.知らないグラフがいっぱいある.困る.今回はバタフライプロットをSGPLOTで書いてみます.我流なのでもっといい方法があるかもしれません.もっと違う書き方あるよってご存じの方はぜひコメントにて教えてください.

バタフライプロットとは以下のような真ん中から両端に向かって伸びる棒グラフです.見たことはありましたが先日初めて名前をしりました.

 
早速上のグラフを書いたプログラムを貼りますが,x軸は頻度なのでまず数えます.頻度を出したデータの内,左側(青い方)に出したいものは-1をかけて頻度を無理やり負の数にします.そのままSGPLOTに読み込ませて,x軸ラベルの表示を正の数に変えれば完成です.
 
proc freq data= sashelp.class noprint;
  tables sex * age / out = V_CLASS ;
run ;
 
data V_BF ;
    set V_CLASS ;
 
    if SEX = "男子" then do ;
        V_CNT = COUNT * -1 ;     /*頻度を負の値に変換*/
        V_SEX = 1 ;
    end ;

    if SEX = "女子" then do ;
        V_CNT = COUNT ;     /*頻度はそのまま*/
        V_SEX = 2 ;
    end ;
run ;
 
proc sgplot data = V_BF  ;
    hbar AGE / response=V_CNT group = V_SEX barwidth = 1.0;
 
    xaxis
        values=(-5 to 5 by 1)  /*データ上は-5から5まで*/
        valuesdisplay=("5" "4" "3" "2" "1" "0" "1" "2" "3" "4" "5") /*メモリの表示を指定*/
    ;
    yaxis
        values = (10 to 20 by 1)
        grid   /*格子線あった方が多分見やすい*/
        reverse  /*y軸の値の並びを大から小にする*/
    ;
run ;
 
マイナスでデータを持たせて左右に表示して,軸のメモリ表示は正の値として指定するのってなんだかイマイチな気がします.わざわざ手でメモリ値を指定するのて結構煩雑ですし…いい感じに指定する方法があればいいんですが,どなたかご存じないですか?

 

2020年7月1日水曜日

defineのピナクルチェックのerrorですげーてこずった話

気づけば簡単なんですが,errorの原因に気づかずに時間かかったのでここで供養します.なむなむ

defineをピナクルでチェックすると以下のerrorが出ました.ほうほう参考資料が無いのねと思って確認すると,確かにdefineにリンク貼り忘れたものがあったので追加しても以下のerrorは消えなかったのです.

Pinnacle 21 ID:DD0015
Message       :Referenced Document is missing
Severity      :Error
Found         :1

何で消えないのかわからなかったのでreportのdetailを見に行くと以下の通り.
最初から見に行けと言う話は横に置いて,error箇所はdefineの最終行,valuesはnullと何のことかさっぱりです.足りない参考資料は入れたので参考資料が無いよ!のnullではないのは明らかです.

Variables:Line Number: defineのソースコードの最終行
Values   :null

正解はdefineの参考資料をリンクする際に記載するIDに誤記があったことでした.誤記していたためそんなID無いよ!ってerrorを出していたようです.
defineに参考資料をリンクする際には,最初の<def:SupplementalDoc>タグで参考資料のIDを定義して,本文中でそのIDを参照します.

正しい状態だと以下になります.leafIDが1回目と2回目でhogehogeと一致しています.
<def:SupplementalDoc>
    <def:DocumentRef leafID="hogehoge"/>
</def:SupplementalDoc>

<def:DocumentRef leafID="hogehoge">
</def:DocumentRef>

今回のerrorの原因は以下の状態になっていました.
後半で指定したpiypiyoはSupplementalDocタグで指定していないです.
<def:SupplementalDoc>
    <def:DocumentRef leafID="hogehoge"/>
</def:SupplementalDoc>

<def:DocumentRef leafID="piyopiyo">
</def:DocumentRef>

誤記をするなと言う話なのですが,末尾に1つだけerrorを返すのではなくせめて一致しないIDの数くらい教えてほしいです…今回は誤記があったのが3つだったので,最低でも3つはあったはずです…1つしかerrorはないって言われていたので,defineの中で1つしかなさそうなものからerrorの原因を探していました…

2020年6月15日月曜日

sgplotでemfファイルを出力する話

何回やっても次やるときにすっかり忘れて自分のプログラムを探す羽目になるのでここをメモ代わりにします.私以外の人に需要は無いと思いますがそんなことは知らぬ

sas9.4のsgplotで出力をemfファイルで出したい時の設定です.いっつもpngとかで出しがちなので覚えてないんですよね…emfにもいいところはあるのですが.

以下プログラム

/*----- 適当なデータ -----*/
data hoge ;
    call streaminit(1234) ;
    do X = 1 to 36 by 1 ;
        Y = int(rand("uniform") * 100) ;
        output ;
    end ;
run ;

ods html close ;   *--htmlをcloseしないとwarninig出る;
ods listing gpath = "hogehoge" ;
ods graphics on / reset = all imagename="hist1" imagefmt=emf ;

proc sgplot data = hoge nowall noborder ;
    vbar X / response = Y ;

    xaxis
        values = (1 to 25 by 1)
        label="発現時期"
    ;

    yaxis
        label="発現率(%)"
        values = (0 to 100 by 20)
    ;

run ;

ods graphics off ;
ods listing close ;
ods html ;  *-- 元に戻す ;

2020年6月1日月曜日

複数の文字変数をつないだら文字切れするのを回避する話

ついに月一の更新すら怪しくなったブログはこちらです.
コロナで生活リズム変わったからね仕方ないね
 
先日私の身辺でちょっと話題になったことをテーマにします.
フリーテキストのコメントを一覧表かなんかに出力する時に,想定より長いコメントが来て途中までしか出力出来てなかったことがありました.一覧表と言えばページ数がかさむので,途中で切れてないか目視チェックは結構厳しいところがあります.安易に変数長を200byteまでにすると200を超える長さのコメントが来るとか,出力の際に()付けたりとか複数のコメントを結合させて1か所に出せとか言われるとぽろっと後ろが切れたりします.お薬の英語名と日本語名と記載名とかもそうですが…

さりとて切れてるのがあるとお叱りを受けてしまうので何とかしないといけません.出力用の変数が十分な長さを持っているかをlengthnで取得して長さを比較すれば確認できますね.
 
data _null_ ;
    /*----- 元のコメント -----*/
    A = "日本語のコメントだよ" ;
    B = "qwertyuiop";
 
    /*----- 出力用変数 -----*/
    length OUT $24 ;
    OUT = A ||  "/" ||  B ;
 
    /*----- 各変数の長さを求める -----*/
    V_A = lengthn(A) ;
    V_B = lengthn(B) ;
    V_OUT = lengthn(OUT) ;
 
    /*----- 元の変数と区切り文字の長さの合計が出力用変数より長い場合にobs番号をログに表示 -----*/
    if V_A + V_B + 1 > V_OUT then putlog "E" "RROR/" _n_ ;
run ;
 
ってタラタラ書きましたけどさあcat系の関数使ったら結合後の文字変数の長さが足りない時はwarning返るよねえ…文字の結合を||でやってんのかね…

241
242  data _null_ ;
243
244     /*----- 元のコメント -----*/
245     A = "日本語のコメントだよ" ;
246     B = "qwertyuiop";
247
248     length hoge hoge2 hoge3 $24 ;
249     hoge = cat(A, "/" , B) ;
250     hoge2 = cats(A, "/" , B) ;
251     hoge3 = catx("/" , A , B) ;
252
253  run ;
WARNING: CAT関数の呼び出しで、結果に割り当てられたバッファの長さが十分ではなかったため、すべての引数を連結して含めることができませんでした。
         正しい結果には31文字が含まれますが、実際の結果は、呼び出し環境によって、24文字に切り捨てられた、またはすべてブランクの可能性があります。
         どの引数以降が切り捨てられたかを次に示します。
NOTE: 第3引数(関数CAT('日本語のコメントだよ','/','qwertyuiop'):行 249 カラム 8)は無効です。
WARNING: CATS関数の呼び出しで、結果に割り当てられたバッファの長さが十分ではなかったため、すべての引数を連結して含めることができませんでした。
         正しい結果には31文字が含まれますが、実際の結果は、呼び出し環境によって、24文字に切り捨てられた、またはすべてブランクの可能性があります。
         どの引数以降が切り捨てられたかを次に示します。
NOTE: 第3引数(関数CATS('日本語のコメントだよ','/','qwertyuiop'):行 250 カラム 9)は無効です。
WARNING: CATX関数の呼び出しで、結果に割り当てられたバッファの長さが十分ではなかったため、すべての引数を連結して含めることができませんでした。
         正しい結果には31文字が含まれますが、実際の結果は、呼び出し環境によって、24文字に切り捨てられた、またはすべてブランクの可能性があります。
         どの引数以降が切り捨てられたかを次に示します。
NOTE: 第3引数(関数CATX('/','日本語のコメントだよ','qwertyuiop'):行 251 カラム 9)は無効です。
A=日本語のコメントだよ B=qwertyuiop hoge=  hoge2=  hoge3=  _ERROR_=1 _N_=1
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.01 秒

2020年4月1日水曜日

includeで読み込んだプログラムファイルのパスとファイル名を取得する話

%includeで読み込んだプログラムのパスとファイル名を保持している自動マクロ変数を見つけたので紹介です.でもちょっと使い道が思いつかないので良い感じのがあればぜひ教えてください

変数の名前はSYSINCLUDEFILEDIR とSYSINCLUDEFILENAME です.長いので覚えられないですね.これをプログラムに仕込んでおいて,その仕込んだプログラムをincludeすると値が取得できます.平文で書いてもマクロ変数の中身は空なのでincludeしている時に値を取得している感じですかね.errorにならないのはとても良い

例えば以下の簡単なプログラムをtst.sasと名前を付けてincludeすると,ログにプログラム名とパスが表示されます.表示されるんですが...使い道思いつかないですね...ファイル名に関しては実行しているプログラム内で自分のファイル名を取れるのでそこは便利か...?

/*---------- includeしたプログラム(ファイル名はtst.sas) ----------*/
/*読み込み成功*/
%put &=SYSINCLUDEFILEDIR ;
%put &=SYSINCLUDEFILENAME ;

/*---------- include ----------*/
data _null_ ;
  rc = dlgcdir("piyopiyo") ;
run ;
%include "tst.sas" / source2;

/*---------- 実行ログ ----------*/
144  data _null_ ;
145
146  rc = dlgcdir("piyopiyo") ;
147
148  run ;
NOTE: 現在の作業ディレクトリは"piyopiyo"です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.01 秒
171
172  %include "tst.sas" / source2 ;
NOTE: %INCLUDE (レベル1)ファイルtst.sasはファイルpiyopiyo\tst.sasです。
173 +/*読み込み成功*/
174 +%put &=SYSINCLUDEFILEDIR ;
SYSINCLUDEFILEDIR=piyopiyo        /*---------- includeしたプログラムのパス ----------*/
177 +%put &=SYSINCLUDEFILENAME ;
SYSINCLUDEFILENAME=tst.sas        /*---------- includeしたプログラムの名前 ----------*/

あと同じようなものにSYSINCLUDEFILEDEVICE とSYSINCLUDEFILEFILEREF もあります.
includeしたファイルが存在するデバイス名と,includeにfilenameステートメントで定義した文字列を指定した時に,そのfilerefを取得するものです.今回はfilenameしてないので空ですね

175 +%put &=SYSINCLUDEFILEDEVICE  ;
SYSINCLUDEFILEDEVICE=DISK       /*-------- ファイルはdiskにいる,それはそうだ. --------*/
176 +%put &=SYSINCLUDEFILEFILEREF ;
SYSINCLUDEFILEFILEREF=               /*---------- 今回は空 ----------*/

filename hogehoge "hoge.sas" ;としてから%include hogehogeを実行すると,SYSINCLUDEFILEFILEREFの値としてhogehogeが取れます.filefilerefってfileが続くのちょっとややこしい