2015年10月27日火曜日

PUT (変数名) (出力時の表示法) という書き方





PUTステートメントで、変数値をログや外部ファイルに出力することができます。
今回はこのPUTステートメントの便利な書き方を紹介します。



以下の構文で、指定した変数すべてに、出力時の表示法を設定できます。

構文
PUT  (変数名1 変数名2 ・・・) (出力時の表示法) ;

または全変数(自動変数以外)を出力対象にしたい場合は

PUT  (_ALL_) (出力時の表示法) ;



例1
data DT1;
  A=1;
  B=2;
  put (_ALL_) (=);
run;

ログ
A=1 B=2

変数にイコール「=」をつけて出力


例2
  put (_ALL_) (=/);

ログ
A=1
B=2

変数にイコール&改行をつけて出力


例3
  put (_ALL_) (+0);

ログ
2

変数値のみ出力


この使い方を覚えてると、ここぞという時にも便利です。

2015年10月22日木曜日

順位をつけるRANKプロシジャと、TIESオプションによる同値の扱い【まとめ】





変数値の小さい順に、順位を与える事ができます。



構文

PROC  RANK   DATA = 読込データ  OUT = 出力データ  TIES = タイ値の扱い ;
      VAR          順位を見る変数 ;
      RANKS     順位を格納する変数 ;
RUN ;



注意点


  • 欠損値には順位がつかない。
  • RANKSを指定しないと、VARの変数に順位が格納される(上書きされる)。
  • 同値があった場合の取り扱いを、TIESオプションで設定。
  • 以下で紹介されている「浮動小数点の誤差」ってやつで微妙に値が異なっていると、別々の順位が割り当てられるのでご注意ください。


具体例


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


TIESオプションの設定別
proc rank data=DT1 out=OUT1 ties=low;
   var A;
   ranks A2;
run;
  A  
  A2  
  10    1  
  10  1
  21  3
  30  4

1位と2位が同値なので、最小の1位が入る。
次の値が3位


proc rank data=DT1 out=OUT2 ties=high;
   var A;
   ranks A2;
run;
  A  
  A2  
  10    2  
  10  2
  21  3
  30  4

1位と2位が同値なので、最大の2位が入る。
次の値が3位


proc rank data=DT1 out=OUT3 ties=mean;
   var A;
   ranks A2;
run;
  A  
  A2  
  10    1.5  
  10  1.5
  21  3
  30  4

1位と2位が同値なので、平均の1.5位が入る。
次の値が3位


proc rank data=DT1 out=OUT4 ties=dense;
   var A;
   ranks A2;
run;
  A  
  A2  
  10    1  
  10  1
  21  2
  30  3

1位と2位が同値なので、1位が入る。
次の値が2位になる。



ちなみに、値が大きい順に順位をつけたい場合は、descendingオプションをつけます。

proc rank data=DT1 out=OUT5 ties=dense descending;
   var A;
   ranks A2;
run;
  A  
  A2  
  10    3  
  10  3
  21  2
  30  1




2015年10月20日火曜日

大文字や小文字に変換する関数


  • 半角文字 → 文字に変換 ・・・ UPCASE関数
  • 半角文字 → 文字に変換 ・・・ LOWCASE関数





data DT1;
   length A A2 B B2 $10.;
   A = "abcdefg";
   A2 = upcase(A);

   B = "ABCDEFG";
   B2 = lowcase(B);
run;


2015年10月19日月曜日

最初の1バイト目だけ大文字に変換する


たとえば「abc def」→「Abc def」のような変換です。


前提条件
・ 最初の1バイト目はアルファベットである事。
・ 対象変数は半角の英数記号のみである事。


サンプルデータ

data DT1;
   length VAL $20.;
   VAL="proc print"; output;
   VAL="data step"; output;
run;

 VAL  
  proc print  
  data step



先頭の1バイト目だけ大文字にする

data DT2;
   length VAL2 VAL3 $20.;
   set DT1;

   * ① SUBSTR関数を使う ;
   VAL2 = upcase(first(VAL)) || substr(VAL,2);

   * ② 正規表現を使う ;
   VAL3 = prxchange('s/(^[a-z])/\U$1/', 1, VAL);
run;

 VAL2  
 VAL3  
 VAL  
 Proc print Proc print  proc print 
 Data step  Data step  data step

① SUBSTR関数を使う方法
FIRST関数は1バイト目を抜き出す関数
つまり1バイト目を抜き出して大文字にしたものと、2バイト目以降の文字を結合しています。

② 正規表現を使う方法
PRXCHANGE関数で正規表現による置換をしてます。
「prxchange('s/(^[a-z])/\U$1/', 1, VAL)」

(^[a-z])」は先頭の文字がa~zのいずれかである場合、、という意味。
\U$1」は上記を大文字に変換しています。


2015年10月15日木曜日

Graph Template Language(GTL)入門:MVARステートメント【マクロ変数を引数にする】

 


まずは構文をご覧ください。



構文

 PROC TEMPLATE ;
      DEFINE STATGRAPH テンプレート名;
            MVAR    マクロ変数1  マクロ変数2 ・・・ ;
            NMVAR  マクロ変数1  マクロ変数2 ・・・ ;

            ~ グラフテンプレート作成処理 ~
      END;
 RUN;

  • MVAR に指定したマクロ変数は、PROC TEMPLATE内で文字の設定値として参照可能になる。
  • NMVAR に指定したマクロ変数は、PROC TEMPLATE内で数値の設定値として参照可能になる。


例を見ていきましょう。


ステップ1・・・ グラフテンプレートの作成

proc template;
    define statgraph MYGRAPH;
    mvar  m_TITLE m_XVAR m_YVAR;
    nmvar  m_XREF;

    begingraph;
         entrytitle m_TITLE;
         layout overlay;
             scatterplot x=m_XVAR y=m_YVAR;
             referenceline x=m_XREF / curvelabel="特に意味はない参照線";
         endlayout;
    endgraph;
    end;
run;

  • mvar m_TITLE m_XVAR m_YVAR」で、マクロ変数「m_TITLE」~「m_YVAR」を文字の設定値として参照可能にする。
  • nmvar m_XREF」で、マクロ変数「m_XREF」を数値の設定値として参照可能にする。
  • entrytitle m_TITLE」で、グラフのタイトルにマクロ変数「m_TITLE」を設定。
  • scatterplot x=m_XVAR y=m_YVAR」で、散布図のX軸にマクロ変数「m_XVAR」、Y軸にマクロ変数「m_YVAR」を設定。
  • referenceline y=m_XREF」で、X軸の参照線にマクロ変数「m_XREF」を設定。



ステップ2・・・ マクロ変数に値を設定して、グラフ作成実行

%let m_TITLE = 散布図;
%let m_XVAR = AGE;
%let m_YVAR = HEIGHT;
%let m_XREF = 12;

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



  • ステップ1で使用しているマクロ変数「m_TITLE」~「m_XREF」に値を設定。
  • PROC SGRENDERでグラフ作成を実行しています。



覚えておきたいポイント


ポイント1
MVAR/NMVARの細かいルールでミスってうまく動かないことが多いので、リファレンスを参照しつつ、実行後にログや出力結果を確認するようにしましょう。

例えば、以下の青字部分をご覧ください。

proc template ;
    define statgraph MYGRAPH;
        nmvar m_XLIST;
        begingraph;
           layout overlay / xaxisopts=(linearopts=
              (viewmin=0 viewmax=200 tickvaluelist=m_XLIST));
                scatterplot x=WEIGHT y=HEIGHT;
           endlayout;
        endgraph;
    end;
run;

%let m_XLIST = 0 100 200;
proc sgrender data=SASHELP.CLASS template=MYGRAPH;
run;




PROC TEMPLATEでは通常「TICKVALUELIST」を以下のようにカッコで囲って数値を列挙しますが、

tickvaluelist=(0 100 200)


NMVARでマクロ変数を設定する場合、カッコは不要。

tickvaluelist=m_XLIST


マクロ変数に値を設定する際も、カッコは不要です。

%let m_XLIST = 0 100 200;



ポイント2
データステップ100万回「マクロ変数に数値をいれて戻すと誤差がでちゃう場合がある問題について考える話
で解説されてるように、マクロ変数に小数をいれると切り捨てられる可能性があるのでご注意ください。


ポイント3
本題と逸れますがマクロ変数は「スコープ」というものを理解していないと、落とし穴にはまってしまうことがあります。スコープについては以下記事を参照下さい。

関数
マクロ変数を引数にする
軸の設定

2015年10月13日火曜日

比較演算子のアルファベット表記と、ちょっとした小技





IF / WHEREステートメントで用いる比較演算子は、アルファベット2文字で書くことができます。

 比較演算子  別の書き方  意味 
 = EQ equal to
 ^= NE not equal to
 > GT greater than
 < LT less than
 >= GE greater than or equal to
 <= LE less than or equal to


たとえば 「if A=1」 は 「if A eq 1」 とも書けます。

海外のSASプログラムを見てると、このアルファベット2文字を使ってる事が結構あります。

各自が好きな書き方でプログラミングを楽しめるのが一番だと思いますが。。
まわりのSASプログラマに聞いてみたら、この書き方は結構受けが悪かったです。




ちょっとした小技

受け悪いって方にも、使えるかもしれないテクニックを紹介。
以下をご覧ください。

%macro SAMPLE( WH );
     data DT1;
          set SASHELP.CLASS;
          where &WH;
     run;
%mend;

%SAMPLE( AGE = 13 );

ログ
 ERROR: キーワードパラメータAGEは、マクロに定義されていません。


詳細は割愛しますが、
%SAMPLE( AGE = 13 )」 で 「AGE = 13」 という引数を指定したわけですが、引数にイコール「=」があると、キーワードパラメータというものとして解釈され、今回の意図とは異なる動きをしてエラーとなってしまいました。


通常の解決方法として、%STR関数等で 「=」 の効力を無効化してやります。

%SAMPLE( %str(AGE = 13) );



そして、もうひとつの解決方法。

%SAMPLE( AGE eq 13 );




アルファベット表記に変えてしまえば、「=」 を使わなくて済むというわけです。



2015年10月9日金曜日

マクロ実行文をログに展開するMPRINTとMPRINTNEST


他の方が書いたSASプログラムを引き継いで運用するって事ありますよね。
そんな時に今回紹介するオプションが結構役に立ちます。


まず以下のプログラムがあったとします。
* 適当な例 ;
%macro TEST;
     X=1;
%mend;

%macro TEST2;
     data DT1;
         %TEST
     run;
%mend;

マクロ[TEST2]の中で別のマクロ[TEST]を呼び出しているプログラムです。


ここで MPRINT オプションを設定してマクロを実行すると、、
options mprint;
%TEST2;


ログ
MPRINT(TEST2):   data DT1;
MPRINT(TEST):   X=1;
MPRINT(TEST2):   run;

上のログのように、展開されたマクロがログに出力されます。


さらにMPRINTNEST オプションも合わせて設定すると、、
options mprint mprintnest;
%TEST2;


ログ
MPRINT(TEST2):   data DT1;
MPRINT(TEST2.TEST):   X=1;
MPRINT(TEST2):   run;

MPRINT(マクロ名1.マクロ名2) という風にネストされているマクロ名がすべて出力されます。

設定を戻すには 「options nomprint nomprintnest ;」と書けばok


マクロって便利ですが、ネストしまくると読みづらいプログラムになりがちです。
そういった時、上記オプションを設定することで、マクロの動きが分かり易くなる場合があります。


2015年10月4日日曜日

「ODS POWERPOINT」によるパワーポイントへの出力(SAS9.4からの機能)


SAS9.4から追加された 「ODS POWERPOINT」 について、ざっくりまとめました。



構文

ODS POWERPOINT  LAYOUT=スライドの種類   FILE="出力ファイルのフルパス OPTIONS( オプション );

         ~ プロシジャ等による出力処理を記述  

ODS POWERPOINT  CLOSE;

スライドの種類
・TitleSlide            : タイトルのみ
・TitleAndContent : タイトルとコンテンツ
・TwoContent        : コンテンツを横に2つ並べる



例 

同じSAS9.4でも、メンテナンスリリースのタイミングで追加されたオプションもあるので、以下の例が正しく動作するかは各環境でご確認下さい。

*** スライド1枚目 ;
ods powerpoint  layout = TitleSlide
   file = "各自の出力するファイルのフルパスを指定"
   options( backgroundimage="linear-gradient(90deg, white, skyblue)" );

   proc odstext ;
      p "適当にスライドを作ってみる"  / style=presentationtitle;
      p "by SAS忘備録" / style=presentationtitle2;
   run;

*** スライド2枚目 ;
ods powerpoint layout = TitleAndContent;

   title "集計";
   proc means data=SASHELP.CLASS n mean std min max;
      var HEIGHT WEIGHT;
      class SEX;
   run;

*** スライド3枚目 ;
ods powerpoint layout = TitleAndContent;

   title "散布図";
   proc sgplot data=SASHELP.CLASS;
      scatter x=HEIGHT y=WEIGHT / group=SEX;
   run;

ods powerpoint close;

出力結果


スライド中のテキストは、SAS9.4から追加されたODSTEXTプロシジャというもので出力します。
なかなか面白いプロシジャですが、詳細は割愛。。


今回はSASのマニュアルのほか、以下SASユーザー総会2015で発表された資料を参考にさせていただきました。

「ODS POWERPOINTの活用:SASからMicrosoft Powerpointへのエクスポート」
https://www.sas.com/content/dam/SAS/ja_jp/doc/event/sas-user-groups/usergroups2015-d-04.pdf

最新の機能(ODSTEXT・ODSLIST・DOCUMENTプロシジャ)なども合わせて紹介されていて、私の大好きな発表です。


2015年10月1日木曜日

暗黙のOUTPUTステートメント



今回の内容は、PDV(プログラムデータベクトル)を理解していないと、正確な動きを理解しづらいかも。

あと、データステップ中にMODIFYステートメントを使用していない前提での説明となります無限ループになったり挙動が変わることがあるので・・)



サンプルデータ

data DT1;
   length A 8.;
   A=1; output;
   A=2; output;
run;

  A  
  1
  2



暗黙のOUTPUT


以下は単純なデータステップですが、、

data OUT1;
   set DT1;
run;

内部では、SETに指定した「DT1」から1オブザベーション毎にPDV(読み込んだデータを入れておく一時的な箱)で処理してから、出力データセット「OUT1」に出力していて、
この処理の流れが「DT1」から1オブザベーションずつ、全オブザベーションに対して反復されます。


そして実は、データステップの各反復の最後にOUTPUTステートメントが自動で実行されています。
(以下はあくまでもイメージ)

data OUT1;
   set DT1;
   output;
run;


OUTPUTステートメントとは、
読み込んだオブザベーションに対する処理結果を、データセットに出力するステートメントです。

つまりOUTPUTが自動で実行されるおかげで、データセットへの出力ができているわけです。
(これを「暗黙のOUTPUT」と呼びます)



例外


以下のようにプログラマ側が明示的にOUTPUTステートメントを記述した場合、、

data OUT2;
   set DT1;
   if A=2 then output;
run;

  A  
  2

明示的に記述したOUTPUTが優先され、暗黙のOUTPUTは実行されなくなります。



注意


以下の実行結果は想定通りでしょうか?

data OUT3;
   length A B C 8.;
   A=1;
   B=1;
   output;
   C=1;
run;

 A  
 B  
 C  
  1  1  .

① まず「output;」によって、明示的にOUTPUTを行うタイミングを変えています。
② 次に「C=1;」とありますが、①で既にデータセットへの出力が済んでるため、この処理結果はデータセットに反映されません。よってCは欠損値になります(変数自体は作られます。データステップで作成した変数は記述位置に関係なく変数の枠が最初に作られるので)


ちなみに、明示的なOUTPUTを複数記述することもできます。

data OUT4;
   length A B C 8.;
   A=1;
   B=1;
   output;
   C=1;
   output;
run;

 A  
 B  
 C  
  1  1  .
  1  1  1

OUTPUTする度に、データセットにオブベーションが追加され、
2個目のOUTPUTで「C=1;」が反映されたオブザベーションが追加されています。