2016年12月27日火曜日

Graph Template Language(GTL)入門:軸の設定



GTLでX軸とY軸を設定する方法をまとめました。

ここでは「LAYOUT OVERLAY」内で軸を設定する方法を紹介します。
(LAYOUTステートメントの種類によって設定が異なる)



まず以下のように軸の場所によってオプションの名前が決まっています。





構文
PROC TEMPLATE ;
   DEFINE STATGRAPH テンプレート名;
   BEGINGRAPH ;
        LAYOUTステートメント  /  XAXISOPTS  = ( x軸オプション )
                                                    YAXISOPTS  = ( y軸オプション )
                                                    X2AXISOPTS = ( x2軸オプション )
                                                    Y2AXISOPTS = ( y2軸オプション )
                                                    WALLDISPLAY = NONE    /* 未使用の軸を消す */ ;

                    PLOTステートメント    /  XAXIS=X または X2    YAXIS=Y または Y2;     /* 使用する軸 */

        ENDLAYOUT;
   ENDGRAPH;
   END;
RUN;

PLOTステートメントで、使用する軸を設定できます(デフォルトは「XAXIS=X YAXIS=Y」)



軸オプション  (一部抜粋) 


オプションの組み合わせ・データの性質等で、挙動が変わったり、動作しないことが頻発しがちです。
リファレンスを見ながらトライ&エラーで書いていく感じになります。

設定内容設定( 以下「|」 は構文ではなく「または」の意味で記載)
軸に表示するパーツ

display = line | label | ticks | tickvalues ) | none

(複数指定可。noneの場合だけカッコで囲む必要なし。
このオプションが未指定だと全パーツが表示される)
反対側の軸表示するパーツ

displaysecondary = line | label | ticks | tickvalues )

(複数指定可)
軸ラベル

label = 'ラベル'

(ラベルの文字数が多いとデフォルトでは文字切れを起こす)
軸ラベル位置 

labelposition left | right | top | bottom

(left, rightはx,x2軸のみ、top, bottomはy,y2軸のみ指定可)
軸ラベル書式

labelattrs = (
      family = 'フォント'
      size = サイズ
      color = 
)
目盛の値の書式

tickvalueattrs=(
      family = 'フォント'
      size = サイズ
      color = 
)
グラフ内に目盛線を表示

griddisplay = on
軸を線形軸にする

type = linear
線形軸の設定linearopts (
      viewmin  = 軸の範囲(最小)
      viewmax = 軸の範囲(最大)

      tickvaluesequence = (  ※注意事項あり
                 start = 目盛開始値
                 end   = 目盛終了値
                 increment = 目盛間隔
      )

     
 tickvaluelist = (※注意事項あり  目盛値1 目盛値2 ・・・目盛値リスト )

      tickvaluepriority = true
      ・・・tickvaluelist や tickvaluesequence に指定した目盛の
                 最小・最大を軸に含める(詳細は※こちらを参照)

   
   tickvaluefitpolicy = rotate | rotatealways | stagger など
      ・・・ 目盛の文字が入りきらない時に文字の向き等を変える
             (x,x2,y,y2軸で指定できるオプションの値が異なる)

      
tickvaluerotation = vertical・・・目盛の文字を縦向きに
             (x,x2軸のみ指定可。tickvaluefitpolicyと組み合わせて使用する)
)
軸を離散値にする

type = discrete
離散値の設定discreteopts = (
      tickvaluelist = ( "目盛値1" "目盛値2" ・・・目盛値リスト )
             (注意:このオプションを設定した場合、
                指定しなかった目盛値はデータ部分もグラフから削除される)


      tickvalueformat = 目盛に当てるフォーマット

      tickvaluefitpolicy= rotate | rotatealways | stagger | extract など
      ・・・ 目盛の文字が入りきらない時に文字の向き等を変える
             (x,x2,y,y2軸で指定できるオプションの値が異なる)

 
     tickvaluerotation= vertical ・・・目盛の文字を縦向きに 
             (x,x2軸のみ指定可。tickvaluefitpolicyと組み合わせて使用する)
)



proc template ;
   define statgraph MYGRAPH;
      begingraph;
      layout overlay / walldisplay=none
                              xaxisopts=(labelposition=right griddisplay=on linearopts=(viewmin=10 viewmax=20));

        scatterplot x=AGE y=WEIGHT;
   
      endlayout;
   endgraph;
end;
run;

proc sgrender data=SASHELP.CLASS template=MYGRAPH;
run;



2016年12月19日月曜日

SASでクリスマスツリーを描画する2


去年書いた記事 「SASでクリスマスツリーを描いてみる」 を少し進化させてみました。
雪だるまと雪をunicodeで取得して、雪をチラチラさせるアニメーションを追加しました。




以下「The DO Loop」という有名なSASブログでも雪が舞い落ちるアニメーションをSASで作るプログラムを公開していて、面白いです。
「Animate snowfall in SAS」
http://blogs.sas.com/content/iml/2016/12/14/animate-snow-sas.html

今回こちらとちょっと内容被っちゃった上、私のやつの方がショボイです。。


以下が今回作ったプログラムです。一応SAS9.4で動作します。
*** ツリーとゆき描画用データ作成 ;
data SNOW;
  *** ツリー描画用データ作成 ;
  retain X1 0 Y1 1 Y2 0 Y3 -1 Y4 -2 Y5 -4;

  *** ゆき描画用データ作成 ;
  call streaminit(777);
  do scene=1 to 10;
     do X2=-3.5 to 3.5 by 0.2;
        SNOW = rand('uniform')*8-4;
        output;
     end;
  end;
run;


*** 描画 ;
title ;
options  ANIMATION=START  ANIMDURATION=0.5   PRINTERPATH=GIF ;
ods noresults;
ods printer file="出力先のパスを指定\test.gif" ;

%macro ANIMATION;
  %do i=1 %to 10;
  proc sgplot data=SNOW noautolegend;
     where scene=&i;
     styleattrs wallcolor=black;
     symbolchar name=SNOW char='2744'x;
     scatter x=X1 y=Y1 /  markerattrs=(symbol=starfilled size=0.8cm color=yellow);
     scatter x=X1 y=Y2 /  markerattrs=(symbol=trianglefilled size=1cm color=green);
     scatter x=X1 y=Y3 /  markerattrs=(symbol=trianglefilled size=2cm color=green);
     scatter x=X1 y=Y4 /  markerattrs=(symbol=trianglefilled size=3cm color=green);
     scatter x=X1 y=Y5 /  markerattrs=(symbol=squarefilled size=0.6cm color=brown);
     scatter x=X2 y=SNOW / markerattrs=(symbol=SNOW size=1.5cm color=white);
     inset "Happy Holidays(*ESC*){unicode '2603'x}"/ position=bottom textattrs=(color=white size=50cm);
     xaxis min=-3 max=3  display=none;
     yaxis min=-5 max=1  display=none;
  run;
  %end;
%mend;
%ANIMATION;

ods printer close ;
ods results;

options  ANIMATION=STOP ;


2016年12月15日木曜日

GTLで線形軸(LINEAROPTS=オプション)の最小・最大値を設定する際の注意点




よく勘違いする部分なので、まとめておきます。
ここでは「LAYOUT OVERLAY」内で線形軸を設定する場合の説明になります。



まずは軸設定の失敗例を2つ。

失敗例1
「TICKVALUESEQUENCE」や「TICKVALUELIST」で目盛を設定できますが、
たとえば以下の通り目盛「0~30」、目盛間隔「1」 と設定すると、、

proc template ;
   define statgraph MYGRAPH;
      begingraph;
      layout overlay / xaxisopts=(linearopts=(tickvaluesequence=(start=0 end=30 increment=1)));
           scatterplot x=AGE y=WEIGHT;
      endlayout;
      endgraph;
   end;
run;

proc sgrender data=SASHELP.CLASS template=MYGRAPH;
run;



実際のデータ(Age)が「11~16」の範囲にあるため、目盛は0~30ではなく、11~16になっています。


失敗例2
では今度は、目盛 「12~14」、目盛間隔「1」 と設定した場合、

proc template ;
   define statgraph MYGRAPH;
      begingraph;
      layout overlay / xaxisopts=(linearopts=(tickvaluesequence=(start=12 end=14 increment=1)));
           scatterplot x=AGE y=WEIGHT;
      endlayout;
      endgraph;
   end;
run;

proc sgrender data=SASHELP.CLASS template=MYGRAPH;
run;



実際のデータ(Age)は「11~16」なので、軸は11~16の範囲まで設けられますが、目盛としては12~14が表示されます。



よく頂く質問が、『軸の最小・最大値を 「TICKVALUESEQUENCE」や「TICKVALUELIST」で設定したのに、うまく設定できない!』というものです。
上記で説明した通り、 「TICKVALUESEQUENCE」や「TICKVALUELIST」は目盛を設定するだけなので、軸の範囲(最小・最大)も別途設定する必要があります。



以下に軸の設定例を2つ紹介します。
【シチュエーション】軸の最小・最大を0~30、目盛間隔を2に設定したい


軸の設定例1
「TICKVALUEPRIORITY=TRUE」を指定することで、「TICKVALUESEQUENCE」や「TICKVALUELIST」による目盛の最小・最大値を軸に含めることが出来ます。

proc template ;
   define statgraph MYGRAPH;
      begingraph;
      layout overlay / xaxisopts=(linearopts=(tickvaluepriority=true
                                                               tickvaluesequence=(start=0 end=30 increment=2)));
           scatterplot x=AGE y=WEIGHT;
      endlayout;
      endgraph;
   end;
run;

proc sgrender data=SASHELP.CLASS template=MYGRAPH;
run;

ただし「失敗例2」のように、
「TICKVALUESEQUENCE」や「TICKVALUELIST」で設定した目盛の範囲外に、実際のPLOTするデータがある場合、「TICKVALUEPRIORITY=TRUE」は効果なしです(結果は「失敗例2」の出力結果と同様、実際のPLOTするデータの範囲まで軸が拡張される)



軸の設定例2
「VIEWMIN」,「VIEWMAX」で軸の範囲(最小・最大)を設定し、「TICKVALUESEQUENCE」や「TICKVALUELIST」で目盛を設定する。

proc template ;
   define statgraph MYGRAPH;
      begingraph;
      layout overlay / xaxisopts=(linearopts=(viewmin=0 viewmax=30
                                                               tickvaluesequence=(start=0 end=30 increment=2)));
           scatterplot x=AGE y=WEIGHT;
      endlayout;
      endgraph;
   end;
run;

proc sgrender data=SASHELP.CLASS template=MYGRAPH;
run;

注意点として、
「VIEWMIN」「VIEWMAX」で軸の範囲(最小・最大)が固定されるので、実際のPLOTするデータが軸の範囲外にあっても、その範囲外のPLOT部分はグラフに表示されません。


最後に、
他のオプションとの組み合わせによって、今回紹介したオプションの機能が無効または挙動が変わるので、その辺りの細かい部分についてはリファレンスも確認してみて下さい。



2016年12月10日土曜日

SASデータセットを削除するDELETEプロシジャ



SAS9.4から正式に使えるようになったDELETEプロシジャを紹介します。

以前のバージョンでも、マニュアルに載ってないけど実は使えるっていう、知る人ぞ知るプロシジャでした。
正式に認めてもらえるまで大分時間がかかったけど、やっと表舞台に立てて感無量ですね。

このプロシジャでSASデータセットを削除することが出来ます。



以下、SASデータセットを削除する例をいくつか見ていきましょう。


* サンプルデータ作成 ;
data DT1 DT2 DT3 DT4 DT5 DT6 DT7 DT8;
run;


* ① WORKライブラリの DT1 と DT2 を削除 ;
proc delete lib=WORK data=DT1 DT2;
run;

* ② WORKライブラリの DT3~DT5 を削除 ;
proc delete lib=WORK data=DT3-DT5;
run;

* ③ WORK.DT6 を削除 ;
proc delete data=WORK.DT6;
run;

* ④ WORKライブラリの全データセットを削除 ;
proc delete data=WORK._ALL_;
run;

①と②はSAS9.4から可能になった書き方です。


データセット以外のSASファイルを削除する場合は「MEMTYPE=」で削除するデータのタイプを指定します。例えば、以下 DT20, DT21 というVIEWがあったとして、、


data DT20 / view=DT20;
run;
data DT21 / view=DT21;
run;


以下のように書いて削除します。

proc delete lib=WORK data=DT20 DT21 (memtype=view);
run;


ちなみに1回のPROC DELETEで削除できるデータのタイプは1種類のみで、「VIEWとデータセットをいっぺんに削除」みたいな事は出来ませんでした。



注意点

  • 存在しないデータセットを削除しようとするとログにWARNINGが出力されます。
  • 世代データセットや、外部キーを設定したデータセットなど、特殊なデータセットを削除する際の挙動についてはリファレンスをご確認下さい。

2016年11月30日水曜日

PROC ODSTABLEによるレポート出力



HTMLやRTFなどにレポート出力したい場合、REPORTプロシジャを使う事が多いと思います。
また以下のようにTEMPLATEプロシジャとデータステップで出力する方法もあります。

(以降、出力先がHTMLの場合の結果を示します)

proc template ;
  define table test;
    column NAME SEX AGE HEIGHT HEIGHT_CM;

    define HEIGHT;
       style={color=red};
    end;

    define HEIGHT_CM;
       compute as round((HEIGHT*2.54),0.1);
       header="Height (cm)";
       format=8.1;
       style={color=blue};
    end;
  end;
run;

data _null_;
   set SASHELP.CLASS;
   file print ods=(template="TEST");
   put _ods_;
run;




そして、SAS9.3からODSTABLEプロシジャが追加されました。構文はTEMPLATEプロシジャと同じです。

たとえば、上で書いたTEMPLATEプロシジャの青字部分をコピーして、以下のように「proc odstable」と「run」で囲むだけで完成です。

proc odstable data=SASHELP.CLASS ;
    column NAME SEX AGE HEIGHT HEIGHT_CM;

    define HEIGHT;
       style={color=red};
    end;

    define HEIGHT_CM;
       compute as round((HEIGHT*2.54),0.1);
       header="Height (cm)";
       format=8.1;
       style={color=blue};
    end;
run;


TEMPLATEプロシジャのように出力用のデータステップを書かなくていいのが便利ですね。
構文もなんとなくREPORTプロシジャに似てます。


あと便利だなーと思ったのが、以下のような場合。

proc odstable data=SASHELP.CLASS;
    column NAME SEX (HEIGHT WEIGHT) ;
    define HEIGHT;
       header="Height, Weight";
    end;
run;

変数HEIGHTとWEIGHTを縦に並べて出力しています。
REPORTプロシジャだとこれが出来ないんですよね。


注意点

TEMPLATEプロシジャと構文が一緒だけど一部ODSTABLEでは使えない機能があるようです。
また、HTML, RTF, LISTING等の出力先によって挙動が異なったり、動作しない機能などもあると思うので、別途リファレンスを確認したり、挙動確認をして下さい。



ちなみに以下、入門記事も書いています。


2016年11月22日火曜日

【小ネタ】 CARDSは 「RUN;」 が不要



データステップってふつう 「RUN;」 で締めるのがふつうですよね。

data DT1;
  A=1;
run;

RUN; で「終わりか」と判断してデータステップを終わらせてくれます。



しかし以下のようにCARDSステートメントがあると、、

data DT1;
input A;
cards;
1
2
3
;

ログ
NOTE: データセットWORK.DT1は3オブザベーション、1変数です。


RUN; がなくても実行出来ちゃいました。
CARDS4、DATALINES、DATALINES4の各ステートメントでも同様でした。


よく分からないけど、そうなのか。
分かっててもなんとなく違和感あるから RUN; つけちゃうけど。

2016年11月17日木曜日

PROC REPORTでヘッダーを改行するもう一つの方法



以下記事で、ヘッダーを改行する方法を紹介しました。
REPORTプロシジャ入門2:ヘッダーの設定



別の方法として、以下のように書いてもヘッダーを改行できます。
青字部分は通常の改行の書き方で、赤字部分が今回紹介したかった書き方)

proc report data=sashelp.class split="#";
   column name age;
   define name /  display  "ABC#DEF#GHI";
   define age    /  display  "ABC" "DEF" "GHI";
run;



PROC REPORTで、表示する変数の数が多い場合に分割(ページを分ける)方法



PROC REPORTの「PAGEオプション」と「IDオプション」を紹介します。


早速ですが、
以下は特定の変数のところでページを分割する例です。

ods rtf file="出力ファイルのフルパス";

   proc report data=sashelp.class;
       column name sex age height weight;
       define name / display;
       define sex / display;
       define age / display;
       define height / display page;
       define weight / display;
   run;

ods rtf close;




PAGEオプションを指定した変数のところで、ページ分割されます(上の例では変数HEIGHTのところで分割)

注意点として、
他のオプションとの組み合わせによってはPAGEオプションが動作しない場合があります。
また、LISTING出力先とそれ以外の出力先とで挙動に差異があるようです。
詳細はSAS社リファレンスのPAGEオプションのところをご確認下さい。
SAS Help Center: Syntax: PROC REPORT DEFINE Statement



続いて、PAGEオプションと併用すると便利なIDオプションの例。
以下は変数NAME, SEXを「ID変数」として、各ページの左側に固定で表示させています。

ods rtf file="出力ファイルのフルパス";

   proc report data=sashelp.class;
       column name sex age height weight;
       define name / display;
       define sex / display id;
       define age / display;
       define height / display page;
       define weight / display;
   run;

ods rtf close;



COLUMNステートメントに指定された変数の順に「1列目の変数」~「IDオプションを指定した変数」までを、各ページの左側に固定で表示させています。



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への書込みアクセスは拒否されました。



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

2016年9月28日水曜日

変数の順番を変える小技




たとえば変数がA、C、Bの順に並んでいるデータセットがあり、、

data DT1;
   A=1;  C=1;  B=1;
run;
  A  
  C  
  B  
  1  1  1

これをA、B、Cの順に並び替えたいとします。


この場合、以下のように
「属性定義をするステートメント」をデータステップの先頭に入れることで順番を変える事ができます。

data DT2;
   length A B C 8.;
   set DT1;
run;
  A  
  B  
  C  
  1  1  1

ただし、LENGTHステートメントの場合、変数の型とか長さも入れなきゃいけないので面倒です。


そんな時は、FORMATステートメントを使うと楽です。
(必ずデータステップの先頭に入れてください)

data DT2;
   format A B C;
   set DT1;
run;

  A  
  B  
  C  
  1  1  1