2020年12月1日火曜日

PharmaSUG2020 TOKYOに参加した話

 昨年参加してからもう1年がたったことに驚きだが今年もPharmaSUGに参加した.今年はコロナもあってオンライン開催.昨年は前日に東京に入って準備やらしたのが懐かしい.家にいると気楽なのはいいのだが「ピンポーン」と誰か来るのはもはやご愛敬.残念午前はQAを見ないといけなかったので呼び出しに応じることはできず客は去っていった.用があるならまた来るやろ.準備にかこつけて普段会えない方と集まるのはいい機会ではあったのだがこんなご時世なので致し方ない.いややっぱり大都会東京のオサレお店でご飯食べたかった.大いに残念である.こんな時くらいしか東京行かないのに.

今年の演題はコロナコロナ&covid19とこんなご時世なのはPharmaSUGの内容にも及んでいる.せっかくの機会で諸々のプロセスを見直したりとか,伝播の状況を可視化したりとかなかなか面白い発表がたくさんだった.いやなにかわかったようなことを書いてしまったが英語パワーが足りないのは昨年と同じで,内容が全然サッパリと言う所が多かった.後で資料見てこんなんやったっけ?となることもしばしばである.
オンラインでの研修はあまり参加したことがなかったので色々難しいことがあったが,まあ無事終わってやれやれ.もしこの他にオンライン開催のイベントがあれば,参加してどういう運営をしているのかを見てみたい.せっかくのコロナと言う機会なのでいろんな開催形態を知っていきたいところ.オンラインと現地の両方で開催,なんてことが普通になるとド田舎の私にとっては,非常に参加の機会が広がって良いなと思うこの頃である.

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が続くのちょっとややこしい

2020年3月1日日曜日

棒グラフをsgplotで出す話

ある日質問されて答えられなかったのでここに供養します.ピンとこなかった自分が悲しい.

sgplotで棒グラフを書く時のサンプルです.vbarステートメントを使って出力するのですが,詳細は以下のプログラムの通りです.あんまり凝ったことしてなくほんとにただの紹介なのであまり目新しいことはないですが...軸ラベルが適当ですみません

変数Yの平均値を書いているのですが,オプションでmeanを指定すればOKなのと,標準偏差のヒゲをはやしているのですがこれもオプションで書けます.わざわざ出力用データセットに平均値と標準偏差を持たなくて良いのは楽ですね.確認の具合もあるので何でもかんでもオプションで出せばいいという訳ではありませんが…データに持たせた標準偏差を使ってヒゲをannotateで書かそうとする奴は敵です.

/*以下プログラム*/

data HOGE;
    call streaminit(12);
    do i = 1 to 10 ;
        Y = int(rand('uniform') *100) ;
        if 1 <= i <= 5  then GR = 1 ;
        if 6 <= i <= 10 then GR = 2 ;
        output;
    end;
run;

ods graphics / imagename="bar" imagefmt=png ;
ods listing gpath = "hogehoge" ;
ods html close ;

proc sgplot data = hoge noautolegend nowall noborder ;
    styleattrs datacolors = (gray white) ;
    vbar GR / group = GR groupdisplay = cluster
              response = Y stat = mean nozerobars   /*Yの平均値の棒を立てる*/
              barwidth = 0.5
              limits = upper limitstat = stddev          /*Yの標準偏差のひげを生やす*/
    ;
    xaxis type=DISCRETE ;                              /*不要*/
run ;

ods graphics / reset = all ;
ods html ;

2020年2月1日土曜日

現在の作業ディレクトリの変更する関数の話

dlgcdir関数を使えば現在の作業ディレクトリが変更できます.
正しく実行できると戻り値は0になります.

415  /*現在の作業ディレクトリをlogに表示*/
416  data _null_;
417    rc=dlgcdir();  /*()を空にして実行すると現在のディレクトリを表示する*/
418    put rc=;         /*正しく実行できているかの確認_別に不要ではあるが見るのには便利*/
419  run;

NOTE: 現在の作業ディレクトリは"piyopiyo"です。
rc=0    /*正しく実行できているので戻り値が0*/
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒


420
421  /*現在の作業ディレクトリをhogeに変更*/
422  data _null_ ;
423  rc = dlgcdir("hoge") ;  /*()に指定したフォルダを現在のディレクトリとする*/
424  put rc= ;
425  run ;

NOTE: 現在の作業ディレクトリは"hoge"です。
rc=0
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒


現在のディレクトリを変更することで,指定したフォルダ内のプログラムを%incで読み込む際にファイル名の指定だけで済むので記述が短くなって良いですね.もちろん現在のディレクトリを変更しているので,何が指定したフォルダに出力されるかを確認する必要はありますが.

例えば指定したフォルダの中のhoge.sasとhoge2.sasを実行するには%incにファイル名を書くだけでokになります.もちろん実行するプログラムの前にフォルダパスを記載すれば指定していないフォルダのプログラムも実行できますが,タラタラ前に書くのがめんどくさい時もありますし,フォルダパスをマクロ変数に格納して何かのはずみでマクロ変数の上書きが意図せず起きたりするのが結構ややこしい印象です.

%inc "hoge.sas" ;
%inc "hoge2.sas" ;

私は使ったことないですけど便利そうですよね,そのうち使うかもしれません.
 
後はファイルメニューの「プログラムを開く」で,最初に表示されるファイルの場所が現在のディレクトリになれば最高なんですが…今はsasの保存場所かなんかが最初に出るんですよね…最初と言わずとも現在のディレクトリにすぐアクセスできればokなんですが.

2020年1月8日水曜日

sas studioが9.4M6に更新されてて嬉しい話

最近アクセスできなかったsas studioですが,先日復旧したようですね.
復旧したのでとりあえずproc product_statusを実行すると,BASE SASが9.4M6に更新されているじゃないですか.これで思う存分円グラフが試せるというもんです,やったぜ.円グラフの実用性については知りませんねえ…

以下にせっかくなので実行したproc product_statusの結果を置いておきます.
毒にも薬にもなりませんが更新されててとてもうれしい.せっかくなので該当箇所を赤文字にしています.意味などないですが.

この手の更新情報ってどこかで公開されたりとかってないですかね…私が知らないだけですか?

 1          OPTIONS NONOTES NOSTIMER NOSOURCE NOSYNTAXCHECK;
 70        
 71         proc product_status ;
 72         run ;

 For Base SAS Software ...
    Custom version information: 9.4_M6
    Image version information: 9.04.01M6P110718
 For SAS/STAT ...
    Custom version information: 15.1
 For SAS/GRAPH ...
    Custom version information: 9.4_M6
 For SAS/ETS ...
    Custom version information: 15.1
 For SAS/OR ...
    Custom version information: 15.1
    Image version information: 9.04.01M6P050819
 For SAS/IML ...
    Custom version information: 15.1
 For SAS/QC ...
    Custom version information: 15.1
 For SAS/CONNECT ...
    Custom version information: 9.4_M6
 For SAS Enterprise Miner ...
    Custom version information: 15.1
 For SAS Time Series Workspace Macros ...
    Custom version information: 15.1
    Image version information: 9.04.01M5P110718
 For SAS/ACCESS to Postgres ...
    Custom version information: 9.4_M6
 For SAS Integration Technologies ...
    Custom version information: 9.4_M6
 For SAS/Secure 168-bit ...
    Custom version information: 9.41_M3
 For SAS Credit Scoring ...
    Custom version information: 15.1
 For SAS Text Miner ...
    Custom version information: 15.1
 For SAS High-Performance Forecasting ...
    Custom version information: 15.1
 For High Performance Suite ...
    Custom version information: 2.2_M7
 For SAS Forecast Server Mid-Tier ...
    Custom version information: 15.1
    Image version information: 9.04.01M5P110718
 For SAS/ACCESS Interface to PC Files ...
    Custom version information: 9.4_M6
 For SAS/ACCESS Interface to MySQL ...
    Custom version information: 9.4_M6