2015年7月31日金曜日

RWIで集計表を作る


今回のゴールはRWIで以下の表をPDF形式で作成することです。
集計に使うデータセットは「SASHELP.CARS」の先頭80obsとします。

(注意:今回のプログラムには、HTML形式でサポートされていないオプションを使用しています)




ステップ①

  • まずFREQプロシジャで集計し、結果をデータセットに出力します。
  • 結果のデータセットを加工して、RWIで出力しやすい形にします。

*=== 集計 ===========================================;
proc freq data=SASHELP.CARS (obs=80) noprint;
     tables MAKE / out=OUT1;
     tables TYPE / out=OUT2;
run;

*=== 集計結果を出力の形に加工 =======================;
data OUT;
     length NO 8. ITEM1 ITEM2 COL1 COL2 $20.;

     set OUT1 (in=_IN1)
           OUT2 (in=_IN2)
     ;
     *** 項目No.、項目名の導出 ******************;
     NO    = whichn(1,of _IN:);
     ITEM1 = choosec(NO, "Make","Type");
     ITEM2 = coalescec(MAKE,TYPE);

     *** Nとパーセントを出力の形に **************;
     COL1 = cats(COUNT);
     COL2 = cats( "(", put(round(PERCENT,.1), 8.1) ,")" );
run;






ステップ②

  • 上で作ったデータセットをRWIで出力します。

*=== 出力開始 ===========================================;
options nodate;
ods pdf file="出力するファイルのパス\RWI_Sample2.pdf" style = printer;

data _NULL_;
     length LINE $5.;

     set OUT end=EOF;
     by NO ;

     if _N_=1 then do;
         dcl odsout ob();
         ob.title(data:'集計結果');
         ob.table_start();

         *** 項目名を出力 *************************;
         LINE = "LR";
         ob.head_start();
         ob.row_start();
             ob.format_cell(data:"因子", inhibit:LINE, style_attr:"background=white cellwidth=3cm just=l");
             ob.format_cell(data:""       , inhibit:LINE, style_attr:"background=white cellwidth=3cm just=l");
             ob.format_cell(data:"n"     , inhibit:LINE, style_attr:"background=white cellwidth=2cm just=r");
             ob.format_cell(data:"(%)"  , inhibit:LINE, style_attr:"background=white cellwidth=2cm just=l");
         ob.row_end();
         ob.head_end();
     
end;

     *** データの中身を出力 ***********************;
     * 各集計項目の先頭を1行あける。 ;
     if first.NO then do;
         ob.row_start();
         ob.format_cell( data:ITEM1 , inhibit:"BLR", column_span:4, style_attr:"just=l");
         ob.row_end();
     end;

     * データの中身を出力 ;
     * 変数LINEに、行毎の罫線の設定を格納 ;
     LINE = "TBLR";
     if EOF then LINE="TLR";

     ob.row_start();
         ob.format_cell( data:""        , inhibit:LINE, style_attr:"just=l");
         ob.format_cell( data:ITEM2 , inhibit:LINE, style_attr:"just=l");
         ob.format_cell( data:COL1  , inhibit:LINE, style_attr:"just=r");
         ob.format_cell( data:COL2  , inhibit:LINE, style_attr:"just=l");
     ob.row_end();

     * 最後に表作成を終了 ;
     if EOF then ob.table_end();
run;

ods pdf close;


出力結果

  • プログラム中の青字のパスを各時環境のパスに変更してください。
  • SAS9.4より前のバージョンでは「style_attr」の部分をすべて「overrides」に変えて下さい。
  • その他、バージョン毎に構文が変わっている箇所があるので、実行失敗してないかログを要確認。


今回のポイントは format_cellメソッド内の「inhibitオプション」による罫線の細かい指定です。

構文
FORMAT_CELL( INHIBIT:'罫線を非表示にする位置 ' )

罫線を非表示にする位置
T ・・・ 上側
B ・・・ 下側
L ・・・ 左側
R ・・・ 右側

例 : 上下の罫線を非表示にする
FORMAT_CELL( INHIBIT : 'TB' ) ;

注意:現状、inhibitオプションはHTML形式ではサポートされていない


RWIについては、以下の入門シリーズにまとめています。
レポート作成インターフェイス(RWI)入門1


📝注意

記事の中で使用している「_N_」「END=オプション」「FIRST.BY変数」は「サブセット化IF」と一緒に使用すると正しく動かなくなりやすいです。


割り当てられたライブラリまたはファイル参照をログに出力する。


以下のプログラムで、全ての参照名と参照パスがログに出力されます。


ライブラリの確認
  libname _all_  list;



ファイル参照の確認
  filename _all_  list;



システム用の参照も出てきます。

2015年7月30日木曜日

集計結果をデータセット化する場合、MEANSよりSUMAMYプロシジャの方が効率的



サンプル作成

data DT1;
input A B @@;
cards;
3 10  2 20  1 30
;



SUMMARYプロシジャでAの平均値とBの平均値を求めてデータセットに出力したいとします。
これはOUTPUTステートメントに以下の書き方をするだけでokです。

proc summary data=DT1;
   output out=OUT1  mean(A)=A_MEAN  mean(B)=B_MEAN;
run;



構文

OUTPUT OUT=出力データセット名  統計量( 対象変数 ) = 出力変数名 ・・・;



MEANSプロシジャでも同じ書き方が出来るけど、、

proc means data=DT1 noprint;
   output out=OUT1  mean(A)=A_MEAN  mean(B)=B_MEAN;
run;


MEANS vs SUMMARYプロシジャ」で解説した通り、

MEANSプロシジャでは「VARステートメントを省略すると、CLASSステートメント等のその他のステートメントに指定していない全ての数値変数」が裏で集計されているようで、

SUMMARYプロシジャの方が、余計な集計が行われないし、短い文で書けるので効率的です。


TITLEまたはFOOTNOTEを全てクリアする


以前、以下のようなプログラムを見かけました。

* サンプルデータ作成 ;
data a;
   A=1;
run;

* 出力 ;
title "aa";
title2 "bb";
title3 "cc";

proc print; run;

title;
title2;
title3;





タイトルをクリアするために
title;
title2;
title3;
と書いていますが、これは以下のプログラムでまとめてクリアできます。
goptions reset=title;
もしくは
title;

footnoteの場合は、以下のように書けます。
goptions reset=footnote;
もしくは
footnote;


SAS入門


SASを使い始める時に覚えておくとよさそうな記事をまとめました。
順次追加予定。


基礎



実践


集計

PUT vs PUTLOG


両方ともログに文字を出力するステートメントです。

data _NULL_;
   put "aaa";
   putlog "bbb";
run;


ログ
aaa
bbb


同じ機能なら、PUTLOG 要らないじゃんってなりますが、
FILEス テートメントがあると PUT は外部ファイルへの出力に切り替わります。
一方 PUTLOG は変わらずログへの出力のままです。

data _NULL_;
   file "C:\TEST1.txt";
   put "aaa";
   putlog "bbb";
run;


TEST1.txt
aaa

ログ
bbb


上の例のように外部ファイルとログへの出力を同時進行で行いたい場合に使い分けられるわけです。

算術演算子による足し算とSUM関数の違い


SAS覚えはじめの頃に知っておくと便利な違い。

サンプルデータ作成
data DT1;
   A=1;
   B=2;
   C=.;
run;

 A  
 B  
 C  
 1 
 2 
 .


算術演算子とSUM関数の違い
data DT2;
   set DT1;

   * 算術演算子による足し算 ;
   X1=A+B+C;

   * SUM関数 ;
   X2=sum(A,B,C);
run;

 A  
 B  
 C  
 X1 
 X2 
 1 
 2 
 .
 .
 3

算術演算子の場合、足す変数にひとつでも欠損値があると、足し算の結果は欠損値になります。
一方SUM関数の場合、欠損値以外の値を合計した値を返してくれます。

2015年7月29日水曜日

「RENAME A1-A3=A2-A4」みたいに番号をずらせる


ちょっとした事です。

以下に変数名が連番になっているA1、A2、A3があります。
data DT1;
  A1=10;
  A2=20;
  A3=30;
run;

 A1  
 A2  
 A3  
 10 
 20 
 30 

まずこの変数の接頭文字「A」を「B」に変えたい場合、以下のように書ける。
data DT2;
  set DT1;
  rename A1-A3 = B1-B3;
run;

 B1  
 B2  
 B3  
 10 
 20 
 30 

既にSASYAMAさんが解説されています
rename X_1-X_4=Y_1=Y_4 みたいな連番一括指定


次に番号をずらしたい場合、たとえば
A1~A3 をA2~A4 にずらすには以下のように書くことが出来る。
data DT3;
  set DT1;
  rename A1-A3 = A2-A4;
run;

 A2  
 A3  
 A4  
 10 
 20 
 30 


MEANS vs SUMMARYプロシジャ



大人しく控え目な性格のSUMMARYプロシジャは、MEANSプロシジャの影に隠れがちです。
この2つは、ほとんど同じ挙動をしますが、ほんのちょっと違います。


サンプルデータ
data DT1;
input A B;
cards;
1 125
2 10
3 20
;



違い①

以下はMEANS と SUMMARYプロシジャによる実行結果ですが、同じ結果が得られます。

proc means data=DT1 ;
   var A;
run;

proc summary data=DT1 print;
   var A;
run;




異なる点として、SUMMARYプロシジャは「PRINTオプション」を付けないと集計結果が画面に表示されません。

proc summary data=DT1 ;
   var A;
run;

ログ
ERROR: PRINTオプションまたはOUTPUTステートメントがありません。
NOTE: エラーが発生したため、このステップの処理を中止しました。



違い②

以下はVARステートメントを省略した場合の集計結果です。

proc means data=DT1 ;
run;







proc summary data=DT1 print;
run;


VARステートメントを省略すると、、
  • MEANSプロシジャ: CLASSステートメント等、その他のステートメントに指定していない全ての数値変数が集計される。
  • SUMMARYプロシジャ: OBS数が算出される。

SUMMARYプロシジャの方が全体的に最小限の動きしかしていない感じでですね。
まずは今回紹介した性質の違いを覚えておきましょう。



SUMMARYプロシジャが役に立つ例




2015年7月28日火曜日

MEANSまたはSUMMARYプロシジャで使えるIDGROUPオプションの紹介


最近のお気に入りオプションなので紹介したいと思います。

サンプルデータ
data DT1;
input A B$;
cards;
2 aa
1 bb
3 cc
2 dd
. ee
;

サンプルに対して以下のプログラムを実行すると、、
proc summary data=DT1;
   output out=OUT1 idgroup( max(A) out[5] (A B)= );
run;

なんか横一行のデータが出来ました。

IDGROUPオプションの中に注目してみましょう。
max(A) out[5] (A B)=」 で変数Aが大きい順に行を並べ、上位5行のAとBの値をそれぞれ横に転置して表示するようなイメージです。

大きい順で同値がある場合(今回の例では、1行目と4行目のAが両方2)、オブザベーションの並び順で参照していきます(1行目、4行目の順で参照していく)。


注意点1
上の例で「max(A)」だけだとAが欠損値のオブザベーションは選択されません(今回の例では5行目)
欠損値のやつも引っ張ってきたい場合、以下の通りmissingオプションを追加します。

proc summary data=DT1;
   output out=OUT1 idgroup( max(A) missing out[5] (A B)= );
run;

また「min(A)」とすれば変数Aが小さい順になります。


注意点2
制限として1つの変数につき横に並べられるのは100個までのようです。

2015年7月26日日曜日

DS2プロシジャ入門7:ユーザー定義メソッド




ユーザー定義メソッドとは、自分で関数を作れるみたいなやつと思ってください。


構文
  method  メソッド名 ( 引数の定義1 , 引数の定義2 ・・・ )  returns 戻り値の型 ;
          return 戻り値 ;
  end;

いきなり構文みてもよく分かんないので、まずは簡単な例から。




簡単な例

proc ds2;
   data _NULL_ ;

     method TEST( double VAR1 ) returns double;
         return VAR1+10;
     end;
   
     method init();
         dcl double OUTVAR;
         OUTVAR = TEST(100);
         put '******' OUTVAR '******';
     end;
   
   enddata;
   run;
quit;

ログ
****** 110 ******


解説
TESTというメソッドを作って、これに数値を放り込むと、10足した値を返してくれる仕組みを作ってます。

まずは仕組み作り
method TEST( double VAR1 ) returns double;
    return VAR1+10;
end;

① 「method TEST」で TEST というメソッドを作ります。
メソッドに放り込まれてくる数値を 「( double VAR1 )」 でdouble型の変数VAR1が受け取れるようにします。
returns double」でメソッドの結果をdouble型で返すよう指定。

② 「return VAR1+10」で、VAR1に10足した値をメソッドの結果として返しています。

これで仕組み作り完了!今度はこのメソッドを使ってみます。


method init();
    dcl double OUTVAR;
    OUTVAR = TEST(100);
    put '******' OUTVAR '******';
end;

メソッドの使い方は普段使ってる関数と同じです。
OUTVAR = TEST(100)」でTESTメソッドに「100」を渡し、返された値を変数OUTVARに入れています。




実践例

以下はBMIを計算するメソッドを作っています。

proc ds2;
   data DT1 (overwrite=yes);
     dcl double VAR1;

     method BMI( double CM , double KG) returns double;
     CM :身長(cm) , KG :体重(kg) ;
        dcl double BMI;
        BMI = round( KG / ((CM/100)**2) ,.1);
        return BMI;
     end;
 
     method init();
        VAR1 = BMI(156,60);
        put VAR1;
     end;

   enddata;
   run;
quit;

DT1
  VAR1  
   24.7

実践例とは言いつつも、あんまりメソッドにする意味のない例かな、、




注意事項

メソッドの引数を CHAR, VARCHAR, NVARCHAR などの文字変数にする場合、必ずlengthも設定しましょう。

× method TEST( char X ) ・・・;
○ method TEST( char(20) X ) ・・・;

何故かというと、
引数が文字変数でlengthが設定されていないと自動で「length=8」に設定されます。
なので、メソッドを実行するときに、引数に8バイト以上の文字を指定しても、8バイトで切っちゃいます。




DS2プロシジャ入門記事

1: 基本構文
: 変数の宣言
3: 変数属性と配列の定義
4: データ型①

2015年7月23日木曜日

ODS出力時の用紙サイズや余白などを設定するオプション [まとめ]



私自身のメモも兼ねて。

NODATE 
日時を非表示 
PAPERSIZE =
用紙サイズ
ORIENTATION  =


用紙向き
   縦 ・・・ PORTRAIT
   横 ・・・ LANDSCAPE  
TOPMARGIN = 
上側の余白
BOTTOMMARGIN = 
下側の余白
LEFTMARGIN = 
左側の余白
RIGHTMARGIN = 
右側の余白


注意点
オプションによっては、一部のODS出力先しか対応してなかったり、他の設定に影響を及ぼしたり、
オプション同士の組み合わせによっても、うまく設定できなかったり、というケースがあるようなので注意(リファレンスをご確認下さい)


*** サンプルデータ ;
data DT1;
   do i=1 to 10;
       output;
   end;
run;

*** 用紙サイズや余白などを設定 ;
options
   nodate
   papersize = "A4"
   orientation = landscape
   topmargin = 5cm
   bottommargin = 5cm
   leftmargin = 5cm
   rightmargin = 5cm
;

*** RTFに出力 ;
ods rtf file="出力するファイルのパス\test.rtf";
   proc print data=DT1;
   run;
ods rtf close;


出力結果




memorandumさんの記事によると、これらのオプションを設定すると、以下が変更されてしまうようです!

この他、記事の最初の方に注意点として記載しましたが、リファレンスも参照しつつ、各オプションの挙動にご注意下さい。

2015年7月22日水曜日

PUTステートメントで特定の文字を繰り返す小技


小技というほどでもないかも。
まず以下はログにSASバージョンと日時を表示させるプログラムです。

data _NULL_;
   put "==================================================";
   put " SAS Version  : &SYSVER";
   put " 現在日時      : %sysfunc(datetime(),nldatm20)";
   put "==================================================";
run;

ログ

==================================================
 SAS Version  : 9.4
 現在日時      : 2015/07/22 21:12:00
==================================================

(ちなみに自動マクロ変数 「&SYSVER」 でSASバージョンが取得できます。)


ここで、PUTステートメントを以下の青字部分のように置き換えることができます。

data _NULL_;
   put 50 * "=";
   put " SAS Version : &SYSVER";
   put " 現在日時     : %sysfunc(datetime(),nldatm20)";
   put 50 * "=";
run;


put 50 * "="」で、「=」という文字を50回繰り返し出力しています。


構文はこんな感じ。

  PUT  直後の文字を繰り返す回数  *  "文字";




2015年7月18日土曜日

集計結果をデータセットに出力する「ODS TRACE」と「ODS OUTPUT」




「まとめといて!」 という依頼があったので、簡単にまとめたいと思います。

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


サンプルに対してMEANSプロシジャで要約統計量を求めて、その結果をデータセットに吐き出したいとします。


①まずは「ODS TRACE」で出力オブジェクトの名前を調べる

ods trace on;
proc means data=DT1;
   var A;
run;
ods trace off;

ログ

集計プロシジャを「ods trace on;」と「ods trace off;」で囲って実行すると、ログに上のような表示が現れます。
ログに出てきた「名前」のところにあるキーワードを覚えておきましょう。上の例では「Summary」というキーワードになってますね。


②「ODS OUTPUT」で集計結果をデータセットに出力する

ods output Summary=OUT1;
proc means data=DT1;
   var A;
run;
ods output close;

結果データセットOUT1




今度は集計プロシジャを「ods output 名前 出力データセット名;」と「ods output close;」で囲い直します。
名前のところに先ほどのキーワード「Summary」を当てはめます。

実行すると上の通り、見事集計結果のデータセットが出来上がりです。


ここで注意!
  • プロシジャ側でNOPRINTオプション等で結果ビューアへの出力を抑制すると、ODS OUTPUTが使えない(データセットに出力されない)ので注意
  • 同じプロシジャでもSASのバージョンによって出力オブジェクトの名前や出力データの構造が変わってるかもしれません。別のバージョンで実行する場合、その点よく確認した方がいいです。
  • また日本語版や英語版などの違いによって、出力データセット中の格納値もその国の言語になっている場合があるので注意が必要です。

あと、たまに見かけるミスを以下記事で紹介しているので、あわせてご参照ください。



2015年7月16日木曜日

DS2プロシジャ入門6:データの結合

今回はDS2プロシジャでデータセットを結合する方法です。

サンプルデータ
data DT1;
input A B$;
cards;
1 aa
2 bb
3 cc
;

 A 
 B
  1  
  aa 
  2  
  bb  
  3  
  cc  

data DT2;
input A C$;
cards;
1 xx
3 yy
;

 A 
  1  
  xx  
  3  
  yy  



① 縦結合
proc ds2;
  data OUT1 (overwrite=yes);
      method run();
         set DT1 DT2;
      end;
  enddata;
  run;
quit;

 A 
B
C
  1  
  aa 
   
  2  
  bb  
   
  3  
  cc  
   
  1  
   
  xx 
  3  
   
  yy 

SETステートメントはDATAステップと書き方も似てますが、内部の動きや結合結果も同じになるかは不明。
各自色々なパターンでテストしてみた上で利用したほうが良さそうです。


②横結合
proc ds2;
  data OUT2 (overwrite=yes);
      method run();
          set { select coalesce(DT1.A, DT2.A) as A, B ,C
                  from DT1 full join DT2 on DT1.A = DT2.A
                  order by A };
      end;
  enddata;
  run;
quit;

 A 
B
 C 
  1  
  aa 
  xx  
  2  
  bb  
   
  3  
  cc  
  yy 

DS2プロシジャでは、現状MERGEステートメントがありません。
ではどうやってデータの横結合を実現させるかというと、上のようにSETの中でSQL文を書きます。
SQL文なので当然、DATAステップのMERGEステートメントとは挙動が異なります。

これ面白いですよね。基本的にDS2プロシジャではSQLがキーワードになってきます。
通常のデータステップでもこの機能ほしいな~。


(2022/11/06: 追記)
SAS9.4M6から以下のように「DATAステップのMERGE」と似た横結合が出来るようです。
しかし、こちらもDATAステップのMERGEとは挙動が異なるようです。

MERGEステートメント + RETAINオプション
proc ds2;
  data OUT3 (overwrite=yes);
      method run();
          merge DT1 DT2 / retain;
          by A;
      end;
  enddata;
  run;
quit;

2015年7月14日火曜日

DS2プロシジャ入門5:データ型②


詳細は割愛して、押さえておくべきポイントに焦点を当てたいと思います。
色んなデータ型が用意されてますが、よくマニュアルを読まないと気づきにくい落とし穴もあるのでご注意ください!


DECIMAL型

「DOUBLE」では扱えない桁の数値を利用できます。

DECIMAL を宣言する構文
 dcl  decimal ( 全体の桁数 , 小数部分の桁数 )  変数名 ;


※ 「全体の桁数 」は整数と小数を合わせた桁数を指定します。

たとえば 「dcl decimal (5,2)」 と宣言すれば、整数部分は3ケタ、小数部分は2ケタの数値を表すことができます。
この時 「1234.56」 とか宣言以上の桁がある値を入れたらダメなのでご注意してください。


ポイント①

桁の多い数値は、その数値の末尾に「n」を付ける必要があります。
以下の例をご覧ください。
proc ds2;
data DT1 (overwrite=yes);

    dcl decimal(16,6) VAR1 VAR2;

    method init();
        VAR1=1111111111.111112;
        VAR2=1111111111.111112n;
        put VAR1=;
        put VAR2=;
    end;

enddata;
run;
quit;

ログ
 VAR1=1111111111.111100
 VAR2=1111111111.111112

VAR1とVAR2には同じ値を代入しているはずなのに、ログを見るとVAR1の方は切られちゃってますね。
一方末尾に「n」を付けたVAR2は正しく値が入ってます。


ポイント②

上記プログラムの実行ログをよくよく見てみると、以下のようなメッセージが表示されているはずです。

BASE driver, creation of a DECIMAL column has been requested, but is not supported by the BASE driver. A DOUBLE PRECISION column has been created instead.

これは結果をSASデータセット化する際、、DS2プロシジャでしか扱えない型について、通常のSAS上でも扱える型に置き換える旨の脅しメッセージ(だと思います)。

試しに、先ほど作ったデータセットの中身をデータステップでログに出力してみます。
data _NULL_;
  format VAR1 VAR2 best32.;
  set DT1;
  put (VAR:)(=/);
run;

ログ
 VAR1=1111111111.1111
 VAR2=1111111111.11111

VAR2の値が無慈悲にも最後切れちゃってますね。
型が置き換えられた影響です。

どうしようもないので、今後の解説では通常のSAS上で扱える DOUBLE と CHAR のみを用いることにします。


DS2プロシジャ入門記事

1: 基本構文
: 変数の宣言
3: 変数属性と配列の定義
4: データ型①
5: データ型②

2015年7月10日金曜日

Graph Template Language(GTL)入門:基本構文


簡単なグラフであれば、SGPLOTプロシジャなどが使えますが、
デザインをこうしたい、ああしたい、、という場合、カスタマイズしたグラフのテンプレートを作る必要があります。

このテンプレートを作るのが、Graph Template Language(GTL)です。

出来ることが多すぎるので、ざっくり解説したいと思います。
間違ってるところなどがあれば、修正・追記していく予定ですので、ご指摘お願いします。


基本構文

①まずは、TEMPLATEプロシジャでグラフテンプレートを作ります。

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

               ENTRYTITLE "グラフタイトル" ;
               LAYOUT レイアウト種類  ;
                      プロット文  ;
               ENDLAYOUT ;
               ENTRYFOOTNOTE "フットノート" ;

       ENDGRAPH  ;
   END  ;
RUN ;

  DEFINE STATGRAPH テンプレート名 ;
      ~
  END ;
 作成するテンプレート名を指定。
 適当な名前でok。

  BEGINGRAPH ;
      ~
  ENDGRAPH ;
 お決まりの文。開始と終了を明示。


  ENTRYTITLE "タイトル" ;

 グラフのタイトル

  ENTRYFOOTNOTE "フットノート" ;

 グラフのフットノート

  LAYOUT レイアウト種類 ;
      ~
  ENDLAYOUT ;

 複数のプロットを左右に並べたり、重ねたり、、
 等のレイアウトの種類を選択します。

 (ここは別の機会で紹介予定)


②上で作ったテンプレートを、SGRENDERプロシジャで読み込んで、グラフを作ります。

PROC SGRENDER  DATA=プロットするデータセット  TEMPLATE=テンプレート名 ;
RUN;



簡単な例

* Sample Dataset ;
data DT1;
    input DAY WEIGHT;
    cards;
0  85.5
10 79.5
20 75.2
30 70.5
40 65.8
50 72.1
;

* テンプレートを作成 ;
proc template ;
  define statgraph MYTEMP ;
     begingraph;

          entrytitle "ダイエット記録";
          layout overlay;
               seriesplot x=DAY y=WEIGHT / display=all ;
          endlayout;   

     endgraph;
  end;
run;

* テンプレートを使って、グラフを作成 ;
proc sgrender data=DT1 template=MYTEMP ;
run;




(上記「結果のグラフ出てこないよ」って場合は、なんかしらの設定によって出力がオフになっている可能性あり)


解説

基本構文と照らし合わせると理解しやすいと思います。
ただしプログラム中の以下の部分は、別記事で解説予定なので、詳細は割愛します。

layout overlay;
     seriesplot x=DAY y=WEIGHT / display=all ;
endlayout;  

ざっくりですが、
layout overlayでなんかレイアウトを設定してから、seriesplotステートメントを使って時系列にプロットをしてるって感じです。



GTL入門記事一覧

2015年7月7日火曜日

ARRAYステートメントにRETAIN機能を付与する小技




今回は「RETAIN」と「ARRAY」まわりの話になります(この2つよく分からんって方は以下記事を参照)



では本題。以下のようなデータがあったとします。

* サンプルデータ ;
data DT1;
input A B;
cards;
1 10
. .
2 .
. 20
. .
;

 A  
 B  
 1 
 10 
 . 
 . 
 2
 . 
 . 
 20 
 . 
 . 


変数A, Bについて、欠損値であれば前のオブザベーションからRETAINで値をひっぱってきたいとします。↓
(引っ張った結果をそれぞれ変数A2, B2とする。)

 A  
 B 
 A2  
 B2  
 1 
 10 
 1 
 10 
 . 
 . 
 1 
 10 
 2 
 . 
 2
 10 
 . 
 20 
 2
 20 
 . 
 . 
 2
 20 


そこで最初に思いつくのが、以下のように ARRAY と RETAIN を組み合わせる方法。

data DT2;
   set DT1;

   retain A2 B2;

   array AR1(*) A B;
   array AR2(*) A2 B2;

   do i=1 to dim(AR1);
       if AR1(i) ^=. then AR2(i) = AR1(i);
   end;
   drop i;
run;


実はこれ、RETAINを使わなくてもいけちゃいます。
最近マニュアルを読み返して知ったんですが、ARRAYで初期値を与えるとRETAIN機能が付与されるみたいです。

初期値を与える
data DT2;
   set DT1;

   array AR1(*) A B ;
   array AR2(*) A2 B2 ( 2*. ) ;

   do i=1 to dim(AR1);
        if AR1(i) ^=. then AR2(i) = AR1(i);
   end;
   drop i;
run;


 A  
 B 
 A2  
 B2  
 1 
 10 
 1 
 10 
 . 
 . 
 1 
 10 
 2 
 . 
 2
 10 
 . 
 20 
 2
 20 
 . 
 . 
 2
 20 

プログラム青字部分のようにカッコの中に初期値を与えることが出来て、たとえば、
「array AR(*) A B C  ( 10 20 30 );」 と書けば、「A=10 B=20 C=30」 と初期値が与えられます。
「array AR(*) A B C  ( 3*. );」 と書けば、「A=. B=. C=.」 と3つまとめて初期値として欠損値を与えることが出来ます。


ただし!
※ 初期値を与えてRETAIN機能が付与されるのは、RETAINステートメントと同様にデータステップ内で新たに作成した変数のみです。


2015年7月6日月曜日

配列(ARRAYステートメント)入門



ARRAYステートメントによる配列の定義方法を紹介していきます。

(※ 書き方のパターンがたくさんあるので、触りのみに留めます)




構文

ARRAY  配列名( 要素数 )   割り当てる変数 ;




array  AR(3) A B C;


まず「array AR(3)」で、ARという配列を定義し、変数や値を3つ割り当てられるようにしてます。
次の「A B C」でこの配列に変数A, B, Cを割り当てています。

割り当てた変数は「AR(1)」や「AR(2)」というように「配列名(要素番号)」で参照する事が出来ます。






具体例

data DT1;
   array AR(3) A B C;

   AR(1) = 10;
   AR(2) = 20;
   AR(3) = 30;
run;

 A  
 B  
 C  
 10 
 20 
 30 


解説
arrayステートメントで各配列に以下の変数を割り当てています。
AR(1) は変数A
AR(2) は変数B
AR(3) は変数C
そのため、「AR(1) = 10;」 と書くと 「A = 10;」 と書いたのと同じ意味になります。


ちなみに配列に割り当ててる変数が「A B C」の3つなので、当然要素数も3つだろってことで、「array AR(*) A B C;」というように、要素数をアスタリスク(*)で省略して書けます。


ここで注意点!
配列に割り当てる変数は、
  • すべて同じ型である必要があります(全て文字変数、または全て数値変数)
  • 「lengthを定義してない新規文字変数」を配列に割り当てる場合は、先にlengthステートメント等で長さを定義しておくのを忘れずに。



実践例

以下は変数が欠損値であればいっぺんに0に置き換える例です。

* サンプルデータ ;
data SAMPLE1;
input A B C;
cards;
10 . 20
. . 30
;

 A  
 B  
 C  
 10 
 . 
 20 
 . 
 . 
 30 

* 欠損値を0に置き換える ;
data DT2;
   set SAMPLE1;
   array AR(*) A B C;
   do i = 1 to dim(AR);
       if AR(i) = . then AR(i)=0;
   end;
   drop i;
run;

 A  
 B  
 C  
 10 
 0
 20 
 0
 0
 30 


解説
array AR(*) A B C;

まず配列ARに変数A,B,Cを割り当てます。


do i = 1 to dim(AR);
   if AR(i) = . then AR(i)=0;
end;

DIM関数は、配列の要素数を返してくれる関数です。つまり

do i = 1 to dim(AR);
   処理
end;

で、変数 i の値を1~3 (配列の要素数) まで変化させながら中の処理を繰り返します。

中の処理 「if AR(i) = . then AR(i)=0」 は、配列の要素番号を1~3まで変化させながら処理が繰り返されるので、以下のように配列に割り当てた変数全てに処理が行われます。
if A = . then A=0;
if B = . then B=0;
if C = . then C=0;


ARRAYとDOループの組み合わせはよく見かける鉄板コンビです。