2021年10月26日火曜日

ODS OUTPUTで出力結果を結合する①PERSIST=PROC



ODS OUTPUTの「PERSIST=オプション」について、2回に分けて詳しく紹介していきます。

まず1回目は「PERSIST=PROC」について。 



以下の失敗例をご覧ください。

ODS OUTPUTの中にプロシジャを複数記述したけど、最初のプロシジャの結果しかデータセットに出力されませんでした。


😟失敗例

ods output Moments = out1;

    /* データセットに出力される */
    proc univariate data=sashelp.class;
        var height;
        where age=13;
    run;

    /* データセットに出力されない */
    proc univariate data=sashelp.class;
        var weight;
        where age=14;
    run;

ods output close;



そこで「PERSIST=PROC」というオプションの出番です。

以下のように、プロシジャ間で出力オブジェクトの名前が一緒であれば(今回の場合は「Moments」)、結合してデータセットに出力することが出来ます。


😄成功例(PERSIST=PROC)

ods output Moments (persist=proc) = out1;

    proc univariate data=sashelp.class;
        var height;
        where age=13;
    run;

    proc univariate data=sashelp.class;
        var weight;
        where age=14;
    run;

ods output close;



ただし、以下のようにPERSIST=オプションと一緒に「BYステートメント」が使われていると、WARNINGが出て、正しい結果が得られないようです。


😞注意(BYステートメントは使えないっぽい)

ods output Moments (persist=proc) = out1;

    proc univariate data=sashelp.cars;
        var msrp;
        by make;
    run;

    proc univariate data=sashelp.cars;
        var invoice;
        by make;
    run;

ods output close;

--- LOG --------------
WARNING: ODSでは、データセットWORK.OUT1をRUNグループ間で保持できません。
BY変数が含まれているからです。データセットは第1 RUNグループが終了すると終了します。


To be continued...

https://sas-boubi.blogspot.com/2021/11/ods-outputpersistrun.html


2021年7月22日木曜日

Graph Template Language(GTL)入門:凡例の設定 (DISCRETELEGEND)


GTLで凡例を設定する「DISCRETELEGEND」と「MERGEDLEGEND」を紹介します。



凡例の表示

まずは例を示して、その後構文を紹介したいと思います。


例
proc template ;
  define statgraph MYTEMP ;
     begingraph;

          layout overlay;
               scatterplot x=HEIGHT y=WEIGHT / group=SEX name="sca" ;
               regressionplot x=HEIGHT y=WEIGHT / group=SEX name="regr" ;
               discretelegend "sca" "regr" / title="凡例";
          endlayout;

     endgraph;
  end;
run;

proc sort data=SASHELP.CLASS out=CLASS;
  by SEX;
run;
proc sgrender data=CLASS template=MYTEMP ;
run;




  • 「SCATTERPLOT」で凡例の参照名 "sca" を定義しておく
  • 「REGRESSIONPLOT」で凡例の参照名 "regr" を定義しておく
  • 「DISCRETELEGEND」で参照名 "sca" と "regr" を指定してこれらの凡例を表示しています
  • それぞれのPLOTステートメントで「GROUP=SEX」としているので、グループ変数をSEXとして凡例が表示されています。

構文

PROC TEMPLATE ;
   DEFINE STATGRAPH テンプレート名;
   BEGINGRAPH ;
         LAYOUTステートメント;

                PLOTステートメント  /  NAME = "参照名";
                DISCRETELEGEND  "参照名"  / オプション;

         ENDLAYOUT;
   ENDGRAPH;
   END;
RUN;

  • PLOTステートメントの「NAME=」で凡例の参照名を設定 (任意の名前でOK)
  • DISCRETELEGENDで参照名を指定して、凡例を表示します。



DISCRETELEGENDのオプション  (随時追加予定)

注意:
他のオプション・ステートメントとの組み合わせで、オプションが無効になることがあるため、トライ&エラーで正しく動くか確認が必要です。

設定内容設定
凡例タイトル
title = "タイトル"
凡例タイトルのボーダー
titleborder = true | false
凡例タイトルの書式
titleattrs = (
         family = 'フォント'
         size = サイズ
         color = 色
         weight = bold  /* 太字 */
)
凡例の書式

valueattrs = (
         family = 'フォント'
         size = サイズ
         color = 色
         weight = bold  /* 太字 */
)
凡例の書式で文字の大きさを変えたときに
凡例のマーカー等も大きくするか
autoitemsize = true | false
凡例のボーダーborder = true | false
凡例の表示順 (横順か縦順か)order = rowmajor | columnmajor
凡例の縦位置
valign = top | bottom | center
凡例の横位置halign = right | left | center
凡例の背景色backgroundcolor = 色
「order = rowmajor」指定時に
水平方向に表示する凡例エントリの数
across = 数
「order = columnmajor」指定時に
垂直方向に表示する凡例エントリの数
down = 数




凡例の結合


MERGEDLEGENDステートメントは使える条件が限定されますが、凡例を結合 (融合?) させることが出来ます。

例
proc template ;
  define statgraph MYTEMP ;
     begingraph;

          layout overlay;
               scatterplot x=HEIGHT y=WEIGHT / group=SEX name="sca" ;
               regressionplot x=HEIGHT y=WEIGHT / group=SEX name="regr" ;
               mergedlegend "sca" "regr" / title="凡例";
          endlayout;

     endgraph;
  end;
run;

proc sort data=SASHELP.CLASS out=CLASS;
  by SEX;
run;
proc sgrender data=CLASS template=MYTEMP ;
run;



「SCATTERPLOT」と「REGRESSIONPLOT」はそれぞれグループ変数をSEXにしてPLOTしていますが、凡例のマーカーと線をグループごとにくっつけてしまおう、ということでMERGEDLEGENDを使っています。


MERGEDLEGENDが使える条件
  • PLOTステートメントに「GROUP=」で、グループ化した値を凡例に表示している場合
  • マーカーと線を結合する場合のみ?に有効らしい
  • MERGEDLEGENDで指定可能なオプションはDISCRETELEGENDとほぼ同じ


凡例の設定


2021年5月15日土曜日

LAG関数の動き



LAG関数は「nオブザベーション前の値を取得したい」という場合に役立ちます。

ただし、よくある落とし穴もあるので、最後まで(リンクページも含めて)読んで仕組みを理解したほうが安全です。


簡単な例で見ていきましょう。

*** サンプルデータ作成 ;
data DT1;
input A;
cards;
20
30
10
40
;

data OUT1;
   set DT1;
   B = lag(A);
   C = lag2(A);
   D = lag3(A);
run;

 A 
B
C
D
  20  
  .   
  .   
  .   
  30
  20
  .   
  .   
  10
  30
  20
  .
  40
  10  
  30  
  20  



何が起こっているのか、イメージで説明したいと思います。

(自分なりの解釈になっている可能性があるかもしれませんので、あしからず・・)


今回のサンプルプログラムの「C = LAG2( A )」の時の処理のイメージです。LAG関数によって内部では「キュー」と呼ばれる仕組みを利用しています。


  • 上の例は、ラーメン屋にできた行列みたい。
    • ラーメン屋の行列=「キュー」
    • 行列に並ぶ人=「変数A」
    • ラーメン食べれた人=「変数C」
    • 最初のn杯は店員の味見用に作ったラーメンでお客さんには出さないやつ。
    • 最後に並んだ何名かはスープ切れで食べれなかった、、みたいな

  • 「LAG1」の場合だけ「LAG」と書いてもOK



2021年4月4日日曜日

SASで囲碁盤を再現。



ステイホーム中に囲碁を勉強し始めました。

ルールやっと覚えたところで、コンピュータの一番弱い設定のやつに勝ったり負けたりのレベルですが。。強くなって碁盤の上で繰り広げられる熱い戦いを理解できるようになっていきたいです。


てことで、SASで囲碁を再現してみたくなったので勢いで書いてみました。プログラムは勢いで適当に書いたので、ひどい感じです。


/*** 碁石の位置 (y軸は碁盤に合わせて逆に指定[上から1,2,3...]) *******/
data pos;
input black_x black_y_reverse white_x white_y_reverse;
cards;
2 2 8 2
2 8 3 8
3 7 . .
;

/*** 碁盤の線 *********/
data line;
 do line_x=1 to 9;
 do line_y_reverse=1, 9;
   line_grp=10+line_x;
   output;
 end;
 end;
 do line_y_reverse=1 to 9;
 do line_x=1, 9;
   line_grp=20+line_y_reverse;
   output;
 end;
 end;
run;

data igo;
 set line pos;
run;

/*** PLOT *******************/
proc template ;
 define statgraph tmpl_igo ;
   begingraph;

     layout overlay /
        wallcolor=CXFFC864 /* 碁盤の色 */
        aspectratio=1
        xaxisopts=( display=none displaysecondary=(line ticks tickvalues)
                    linearopts=( viewmin=1 viewmax=9
                                 tickvaluesequence=(start=1 end=9 increment=1) ) )
                                  
        yaxisopts=( reverse=true /* y軸の数値の順番を逆 [上から下] にする) */
                    display=(line ticks tickvalues)
                    linearopts=( viewmin=1 viewmax=9
                                 tickvaluesequence=(start=1 end=9 increment=1)
                                 tickdisplaylist=( "一" "二" "三" "四" "五" "六" "七" "八" "九") ) );

        /* 碁盤の線 */
        seriesplot x=line_x y=line_y_reverse / group=line_grp lineattrs=(color=black pattern=solid);
        
        /* 碁石(黒)を配置 */
        scatterplot x=black_x y=black_y_reverse /
                       filledoutlinedmarkers=true
                       markerfillattrs=(color=black) 
                       markeroutlineattrs=(color=black thickness=1)
                       markerattrs=(symbol=circlefilled size=40 )
                       dataskin=matte;

        /* 碁石(白)を配置 */
        scatterplot x=white_x y=white_y_reverse /
                       filledoutlinedmarkers=true
                       markerfillattrs=(color=white) 
                       markeroutlineattrs=(color=gray thickness=1)
                       markerattrs=(symbol=circlefilled size=40 )
                       dataskin=matte;
        
     endlayout;

   endgraph;
 end;
run;

proc sgrender data=igo template=tmpl_igo;
run;




2020年5月5日火曜日

【SETステートメント】データセットを縦結合する






DATAステップの「SETステートメント」で複数のデータセットを縦に結合することができます。





構文

DATA     出力データセット;
    SET    データセット1   データセット2 ・・・;
RUN;

  • 「SET」に結合したいデータセットを列挙
  • 「DATA」に結合後に作成するデータセット名を指定




Sample data

data DT1;
input X Y:$2. Z;
cards;
1 aa 10
2 bb 20
;
data DT2;
input X Y:$1.;
cards;
3 c
4 d
;
XYZ
1aa10
2bb20
XY
3c
4d




例1. SETに1つだけデータセットを指定した場合

data OUT1;
    set DT1;
run;
XYZ
1aa10
2bb20

  • SETに指定されたデータセットと同じ中身のデータセットが作成されます。



例2. SETに複数のデータセットを指定した場合

data OUT2;
    set DT1 DT2;
run;
XYZ
1aa10
2bb20
3c.
4d.

  • SETに指定されたデータセットの順に縦結合されます。
  • SETに指定されたデータセット間で
    • 共通する変数名同士を縦に結合し
    • 片方にしかない変数も残してくれます。



例3. 失敗例

data OUT3;
    set DT2 DT1;
run;
XYZ
3c.
4d.
1a10
2b20

ログ
WARNING: 入力データセットによって、変数Yに複数の長さが指定されています。
 データの切り捨ての原因になります。


  • なんかログに WARNING が出ちゃったし、
  • 結合元の DT1 の1行目の Y が「aa」だったのが、結合後「a」になっちゃってる
  • 結合元の DT1 の2行目の Y が「bb」だったのが、結合後「b」になっちゃってる 


これは以下の重要な性質によるものです
  • SETに指定された最初のデータセットの LENGTH が適用される


今回の例では、変数Y のLENGTHは
  • DT2・・・「1」
  • DT1・・・「2」


そして、SETに指定された最初のデータセットは「DT2」なので、結合後の 変数Y のLENGTHは「1」に設定されてしまい、文字切れが起きたというわけですね。



解決策
data OUT4;
    length Y $2.;
    set DT2 DT1;
run;
XYZ
3c.
4d.
1aa10
2bb20

  • SETの前に「LENGTHステートメント」でLENGTHを再設定してあげればOK



SETステートメントの落とし穴


SASに慣れてきたかたでも、わりと陥りやすい落とし穴。
以下も参考まで(わりと上級者向けの内容ではあるかもです・・)
SETステートメントの落とし穴



2020年3月11日水曜日

PROC COPY の「OVERRIDEオプション」がいい感じ





とにかくこのプログラムを見てほしい。

proc copy in=sashelp out=work override=(keep=name age);
    select class;
run; 



  • SASHELP.CLASSをWORKにコピーしています。
  • OVERRIDE=オプションで、出力データセットに対して変数NAMEとAGEのみをKEEP



こんな感じで、
OVERRIDEオプションは、出力データセットに対して、データセットオプションを設定出来ちゃうんです。




このオプションの面白いところは、以下のような感じで、、

proc copy in=sashelp out=work override=(rename=(weight=wei));
    select class cars;
run;


出力データセットが複数ある場合、そのすべてのデータセットに、データセットオプションを適用できちゃうところ。



こういう、複数のデータセットに一括でオプションを適用するのって、確か他のプロシジャでは出来ないはず。
アイディア次第では結構便利でもあるし、コピーするついでにって感じで、かなり強力な機能だと思いました。



2020年3月2日月曜日

[SGPLOT] 塗りつぶしパターンの設定




SGPLOTの棒グラフやポリゴンの描画などで「塗りつぶしパターン」を設定する方法を紹介。



まずは、塗りつぶしパターンとか気にしないで、普通に棒グラフを描画してみます。

proc sgplot data=sashelp.class;
    vbar age / stat=percent group=age;
run;





次に塗りつぶしパターンを設定してみます。

ods graphics / attrpriority=none;
proc sgplot data=sashelp.class;
    vbar age / stat=percent group=age fillpattern;
run;



  • PLOTステートメントで「FILLPATTERN」を指定すると、塗りつぶしパターンが追加されます。
  • 「ODS GRAPHICS / ATTRPRIORITY=NONE」は以下のリンク記事で詳細を解説しています。
  • https://sas-boubi.blogspot.com/2019/12/sg.html




さらに、もっと見やすくしてみます。

ods graphics / attrpriority=none;
proc sgplot data=sashelp.class;
    styleattrs datafillpatterns=(L3 X3 R3 L5 X5 R5);
    vbar age / stat=percent group=age fillpattern;
run;



  • 「STYLEATTRステートメント」で塗りつぶしパターンの種類を設定しています。
  • 塗りつぶしパターンには「L1~L5」「R1~R5」「X1~X5」が設定できます。




この塗りつぶしパターンを設定するメリットは、

  • カラーだけに依存していない。
  • 白黒印刷しても、塗りつぶしパターンからグループを区別することができる。
  • 色覚障害のかたによりAccessibilityな出力を提供できる。