2016年10月25日火曜日

グラフ描画時にデータセットが作れる



(注意:今回のテクニックにGPLOTは含まれません)



SGPLOTでグラフを書くときに「ODS TRACE ON/OFF」で囲って実行してみましょう。

ods trace on;
proc sgplot data=SASHELP.CLASS;
  vbar AGE;
run;
ods trace off;

ログ

ログを見ると「SGPlot」という出力オブジェクトが作成されている事が分かります。


この出力オブジェクトを以下のように「ODS OUTPUT」に指定してやると、、

ods output SGPlot=OUT1;
proc sgplot data=SASHELP.CLASS;
  vbar AGE;
run;
ods output close;

データセット:OUT1

なんとグラフ描画時に内部的に作られた?データセットを出力する事が出来ます。



役に立つ例

「ODS GRAPHICS ON;」とすると、一部の統計プロシジャで統計量と一緒にグラフも出力されるのをご存知ですか?
(出力先がLISTINGの場合は統計プロシジャの結果にグラフは含まれません)

これらのグラフも上記の方法でデータセット化出来ちゃいます。


例えば
「ODS GRAPHICS ON;」かつ出力先がHTMLになっている状態で、以下のようにPROC LIFETESTを実行すると生存曲線も出力されますが、、

ods trace on;
proc lifetest data=sashelp.bmt  ;
  time t * status(0);
  strata group;
run;
ods trace off;

ログ













結果ビューア



これもグラフ用データセットが出力できます。

ods output SurvivalPlot=OUT1 ;
proc lifetest data=SASHELP.BMT ;
  time T * STATUS(0);
  strata GROUP;
run;
ods output close;

データセット:OUT1



あとでSGPLOTやSGRENDERを使って自分好みの生存曲線を描く時にかなり使い勝手のいいデータセットです!(わりとそのまんま使える)

今まで生存曲線を描画するためのデータセットを作るのって結構面倒でしたが、上記の方法でだいぶ楽になると思います。


2016年10月20日木曜日

テキストファイル(CSVなど)の読み込みと注意点 【INFILE編】



テキストファイルをデータステップで読み込む例です。





読込むCSVファイル … C:\test\sample1.csv
あああ,10,2012/1/1,10:01
,11,,
ううう,12,2015/1/1,


CSVファイルの読み込み
data OUT1;
   attrib
      A length=$20.
      B length=8.
      C length=8.   informat=yymmdd10.  format=yymmdds10.
      D length=8.   informat=time5.         format=time5.
   ;
   infile "C:\test\sample1.csv"  dlm=","  dsd truncover lrecl=30000 firstobs=1;
   input A B C D;
run;


  A  
  B  
 C 
  D  
  あああ   10   2012/01/01   10:01 
     11   .   . 
  ううう   12   2015/01/01   . 



解説

attrib
    A length=$20.
    B length=8.
    C length=8.   informat=yymmdd10.  format=yymmdds10.
    D length=8.   informat=time5.         format=time5.
;

① 読み込んだときの変数属性(lengthやformatなど)を定義しておく。

テキストファイルのデータをSAS日付値や時間値などに変換して読み込む必要がある場合は、別途 informat も定義。


infile "C:\test\sample1.csv" dlm="," dsd truncover lrecl=30000 firstobs=1;


② 読み込むファイルのフルパスを指定します。また以下の細かい設定もしていきます。

dlm=","
テキストファイルの区切り文字をカンマ「,」に設定。各自該当する区切り文字を設定する。

lrecl=30000
 1行に読み込める長さを最大30000バイトに設定している。
この長さが足りないと、途中で文字が切れてしまう事があるので、読み込むデータの長さに応じて設定する。

dsd truncover
 欠損値の取り扱い等に関するオプション。詳細は以下参照

firstobs=
 テキストファイルの何行目から読み込みを開始するか指定。
例えば、テキストファイルの1行目に項目名、2行目にデータが入っている場合は、「firstobs=2」とします。


input A B C D;


③ テキストファイルから読み込んだ値に対して、割り当てる変数を列挙




注意点と補足

「うまく読み込めない」という場合によくあるパターンとして、
EXCEL等でセル内改行が入ってるデータをCSV形式で保存して、それをSASに読み込むという場合。

セル内改行の部分はSAS上でうまく読み込めず、読込む位置がずれてしまう事があります。


また、以下のようなテキストファイルがあったとして、

., xxx

カンマで区切られた1つ目のデータ「.」は特別な指定をしない限り欠損値の意味として読み込まれます。
カンマで区切られた2つ目のデータ「 xxx」は特別な指定をしない限り前方の半角スペースが取り除かれて「xxx」として読み込まれます。



2016年10月19日水曜日

INFILEステートメントの DSD MISSOVER の挙動




「INFILEで外部ファイルを読み込むときに指定する ”DSD MISSOVER” ってどういう役割?」
って質問をよくもらいます。

具体例で各オプションの役割を見ていきましょう。



DSDの役割


外部ファイルからカンマ(,)等の区切り記号毎に値を区切って読み込む際、挙動を細かく設定するオプションです。


例1
読み込むCSVファイル … C:\test\sample1.csv
1,,2


*** DSDあり ;
data OUT1;
  length A B C 8.;
  infile "C:\test\sample1.csv" dlm="," dsd missover ;
  input A B C;
run;
  A  
  B  
 C 
  1   .   2   

*** DSDなし ;
data OUT1;
  length A B C 8.;
  infile "C:\test\sample1.csv" dlm="," missover ;
  input A B C;
run;
  A  
  B  
 C 
  1   2  .   

  • 上記テキストファイルで「1,,2」 というように区切り記号が連続する部分をDSDオプションにより欠損値として扱うよう設定できます。
  • またこの例では分からないですが、DSDを指定することによって、区切り記号のデフォルトがカンマ(,)に変わります(デフォルトは半角スペース)。区切り記号は「DLM=オプション」で変更可能です。


例2
読み込むCSVファイル … C:\test\sample2.csv
1,2,"x, y and z"


*** DSDあり ;
data OUT2;
  length A B 8. C $10.;
  infile "C:\test\sample2.csv" dlm="," dsd missover ;
  input A B C;
run;
  A  
  B  
 C 
  1   2  x, y and z 

*** DSDなし ;
data OUT2;
  length A B 8. C $10.;
  infile "C:\test\sample2.csv" dlm="," missover ;
  input A B C;
run;
  A  
  B  
 C 
  1   2  "x   

DSDの挙動として、
テキストファイルが「"x, y and z"」というようにクォーテーションで囲まれていると、そのクォーテーションを取り除いた上で中の文字全体を区切り記号もろとも1つの変数に読み込むことが出来ます。



MISSOVERの役割


読み込むCSVファイル : C:\test\sample3.csv
1,2
3,4,5


*** MISSOVERあり ;
data OUT3;
  length A B C 8.;
  infile  "C:\test\sample3.csv" dlm="," dsd missover ;
  input A B C;
run;
  A  
  B  
 C 
  1   2  .   
  3   4  5  

*** MISSOVERなし ;
data OUT3;
  length A B C 8.;
  infile  "C:\test\sample3.csv" dlm="," dsd;
  input A B C;
run;
  A  
  B  
 C 
  1   2  3   


input A B C;」 というように変数を3つ割り当てていますが、
テキストファイルの1行目は 「1,2」 となっているので変数Cを割り当てるデータがありません。
(変数Cを欠損値にしたいのであれば 「1,2,」 というように区切り記号を最後にもつける)

なので、SASは足りない部分を次の行から取ってこようとしちゃいます。
MISSOVERは足りない部分を「欠損値で埋める」という処理をしてくれます。



ただし!

MISSOVERより「TRUNCOVER」というオプションを使用すべき場合があるのでご注意ください
(以下、海外のPaper参照。挙動が分かりやすいです)




一応、軽く説明。

読み込むテキストファイル : C:\test\sample4.txt
a xxx
b yy
c z

*** オプション指定なし ;
data OUT4;
  length A B $10.;
  infile  "C:\test\sample4.txt";
  input A 1-2 B 3-5;
run;
 A  
 B  
 a  xxx 
 b c z

*** MISSOVER ;
data OUT4;
  length A B $10.;
  infile  "C:\test\sample4.txt" missover ;
  input A 1-2 B 3-5;
run;
 A  
 B  
 a  xxx 
 b 
 c 

*** TRUNCOVER ;
data OUT4;
  length A B $10.;
  infile  "C:\test\sample4.txt" truncover ;
  input A 1-2 B 3-5;
run;
 A  
 B  
 a  xxx 
 b yy
 c z


今回は「input A 1-2 B 3-5;」みたいに、カラム(バイト)指定して読み込んでいますが、「TRUNCOVER」以外は臨んだ結果になっていません。

原因として、INPUTステートメントで「B 3-5」として3~5バイト目を読み込んで変数Bに格納するよう指定していますが、以下の通りテキストファイルの2行目と3行目は5バイト目がありません。

a xxx
b yy
c z

  • 読み込むバイト数が足りない場合、そのデータの読み込みがスキップされて、上で示した「オプション指定なし」の結果のようにデータがズレた感じで読み込まれちゃいます。
  • 「MISSOVER」を指定すると、データのズレはなくなりますが、バイト数が足りない部分は欠損値になっています。
  • 「TRUNCOVER」を指定すると、データのズレもなく、バイト数が足りない部分も読み込んでくれています。

ちなみに、以下のように後ろを半角スペースで埋めていれば、バイト数が足りないとは判定されません。

a xxx
b yy 
c z  


このように、INFILE・INPUTの指定方法(と、もしかしたら環境)によって挙動が様々なので、都度挙動の確認はした方がよいです。
ちなみに今回のようなデータをCARDSステートメントで読み込む場合はまた挙動が変わります。

なんてややこしい仕様だ!


2016年10月13日木曜日

ログに空白行を挿入するSKIPステートメント




ほぼ存在を知られていないであろうSKIPステートメントにスポットライトを当ててみようと思います。


構文

  SKIP 空白行の数;



たとえば 「SKIP 5;」 とすればログに5行空白行を挿入できます。



%put aaa;
skip 5;
%put bbb;


ログ

 70         %put aaa;
 aaa
 
 
 
 
 
 72         %put bbb;
 bbb



役に立つ例ってそんなにないですが、
昔、誰かが作ったマクロを実行したらエラー出まくりだったんで、以下のようにログを確認しやすくする目的で、SKIPを使ったことがありました。



%macro TEST(DS);
   data &DS;
   run;

   skip 3;
%mend;
%TEST(DT1);
%TEST(DT2);
%TEST(DT3);


ログ
 69         %macro TEST(DS);
 70            data &DS;
 71            run;
 72         
 73            skip 3;
 74         %mend;
 75         %TEST(DT1);
 
 NOTE: データセットWORK.DT1は1オブザベーション、0変数です。
 NOTE: DATA ステートメント処理(合計処理時間):
       処理時間           0.00 秒
       ユーザーCPU時間    0.00 秒
       システムCPU時間    0.00 秒
       メモリ             555.43k
       OSメモリ           20384.00k
       タイムスタンプ     2023/11/27 午後04:33:13
       ステップ数                        37  スイッチ数  2
       ページフォルト回数                0
       ページリクレーム回数              107
       ページスワップ回数                0
       自発的コンテキストスイッチ回数    11
       非自発的コンテキストスイッチ回数  0
       ブロック入力操作回数              0
       ブロック出力操作回数              160
       
 
 
 
 
 76         %TEST(DT2);
 
 NOTE: データセットWORK.DT2は1オブザベーション、0変数です。
 NOTE: DATA ステートメント処理(合計処理時間):
       処理時間           0.00 秒
       ユーザーCPU時間    0.00 秒
       システムCPU時間    0.00 秒
       メモリ             556.43k
       OSメモリ           20384.00k
       タイムスタンプ     2023/11/27 午後04:33:13
       ステップ数                        38  スイッチ数  2
       ページフォルト回数                0
       ページリクレーム回数              30
       ページスワップ回数                0
       自発的コンテキストスイッチ回数    10
       非自発的コンテキストスイッチ回数  0
       ブロック入力操作回数              0
       ブロック出力操作回数              160
       
 
 
 
 
 77         %TEST(DT3);
 
 NOTE: データセットWORK.DT3は1オブザベーション、0変数です。
 NOTE: DATA ステートメント処理(合計処理時間):
       処理時間           0.00 秒
       ユーザーCPU時間    0.00 秒
       システムCPU時間    0.00 秒
       メモリ             555.43k
       OSメモリ           20384.00k
       タイムスタンプ     2023/11/27 午後04:33:13
       ステップ数                        39  スイッチ数  2
       ページフォルト回数                0
       ページリクレーム回数              30
       ページスワップ回数                0
       自発的コンテキストスイッチ回数    12
       非自発的コンテキストスイッチ回数  0
       ブロック入力操作回数              0
       ブロック出力操作回数              160
       
 
 
 
 
 78 


上の例では、マクロを3回実行していますが、ログに空白行をいれたことで、なんとなく見やすくなって、、い、、、ますね!



2016年10月12日水曜日

「LIBNAME ACCESS=READONLY」で読み取り専用ライブラリにする。


今日身近な人から 「データセットが上書きできない!なんでや!」 って質問いただきました。


答えは簡単で、以下のようにLIBNAMEステートメントに「ACCESS=READONLY」が設定されていたため、読み取り専用のライブラリ参照になっていたのが原因でした。


libname MYLIB "C:\TEST" access=readonly;

data MYLIB.DT1;
   set SASHELP.CLASS;
run;

ログ
ERROR: メンバーMYLIB.DT1.DATAへの書込みアクセスは拒否されました。



質問いただいた人は無事すぐに解決となりました。
私自身もそうですが、結構こういう簡単な部分に気付かず何十分無駄にするってことありますよね。