ラベル マクロ の投稿を表示しています。 すべての投稿を表示
ラベル マクロ の投稿を表示しています。 すべての投稿を表示

2017年8月28日月曜日

そのマクロ変数、ローカル?グローバル?⑤マクロ変数の作成場所を明示的に指定する




マクロ変数を作成する場所(ローカル or グローバル)を明示的に指定することが出来ます。




「%LOCAL」と「%GLOBAL」を使う


・「%LOCALステートメント」で「ローカルマクロ変数」を作成できます。
・「%GLOBALステートメント」で「グローバルマクロ変数」を作成できます。


構文

  %LOCAL    マクロ変数1  マクロ変数2 ・・・;
  %GLOBAL  マクロ変数1  マクロ変数2 ・・・;




使用例の前にまずは、今回つかうマクロ変数をリセット(削除)しときます。
%symdel  MAC1 MAC2 MAC3 MAC4 MAC5  /  nowarn;



%macro TEST;

    %local    MAC1  MAC2 ;
    %global  MAC3 ;

    %put _user_;

%mend;
%TEST;

ログ
TEST MAC1
TEST MAC2
GLOBAL MAC3





「CALL SYMPUTX」を使う


CALL SYMPUTXの第3引数で、ローカル・グローバルどちらに作成するか指定できます。


構文

  CALL SYMPUTX( "マクロ変数名", "格納する値" , "G") ;
  CALL SYMPUTX( "マクロ変数名", "格納する値" , "L") ;


"G": グローバルマクロ変数にする
"L": ローカルマクロ変数にする(マクロ外で実行するとグローバルマクロ変数になる)


%macro TEST;

    data _null_;
        call symputx("MAC4","abcd","L");
        call symputx("MAC5","1234","G");
    run;

    %put _user_;

%mend;
%TEST;

ログ
TEST MAC4 abcd
GLOBAL MAC5 1234


ローカルマクロ変数はマクロ終了とともに消えるので、無駄なマクロ変数が残りません。
なのでローカルマクロ変数を明示的に定義する方が内部的にはキレイなプログラムといえると思います。



記事一覧

1. 基本概念
2. %LET編
3. CALL SYMPUT編
4. マクロ変数の展開
5. マクロ変数の作成場所を明示的に指定する

2017年8月17日木曜日

そのマクロ変数、ローカル?グローバル?④マクロ変数の展開



「ローカル」 と 「グローバル」 に同名のマクロ変数が存在する場合、どちらを優先して展開するのか? という問題です。




フロー図


マクロ変数の展開を「マクロ内」で行っているのか、「マクロの外」で行っているのかによって、以下のとおり展開結果が変わります。





文章でまとめると、
・「マクロ外」では「グローバルマクロ変数」 が展開される。
・「マクロ内」では「ローカルマクロ変数」→「グローバルマクロ変数」 の優先順位で展開される。




具体例


たとえば以下では、同じ名前のローカル・グローバルマクロ変数を定義しています。

%macro TEST;

    data _null_;
          call symputx( "MAC1", "1234", "L" );
          call symputx( "MAC1", "abcd", "G" );
    run;

    %put _user_;

%mend;
%TEST;

ログ
TEST MAC1 1234
GLOBAL MAC1 abcd

(※ CALL SYMPUTXでローカル・グローバルマクロ変数を分けて作る方法は以下で紹介済み。また次回記事でも紹介予定。)





では以下のように、%PUTを使って、マクロ内とマクロ外でマクロ変数を展開してログに出力してみます。

%macro TEST;

    data _null_;
          call symputx("MAC1","1234","L");
          call symputx("MAC1","abcd","G");
    run;

    %put マクロ内 ---> &MAC1;

%mend;
%TEST;

%put マクロ外 ---> &MAC1;


ログ
マクロ内 ---> 1234
マクロ外 ---> abcd

ログを見て頂くと分かる通り、結果はフロー図で示した通りですね。


2017年8月10日木曜日

そのマクロ変数、ローカル?グローバル?③CALL SYMPUT編



「CALL SYMPUT」 または 「CALL SYMPUTX」 で定義したマクロ変数が、ローカル・グローバルどちらになるのか解説します。


まずは前提知識。




シンボルテーブル


マクロ変数を入れる箱の事をシンボルテーブルといいます。

・ローカルシンボルテーブル ・・・ ローカルマクロ変数を入れる箱
・グローバルシンボルテーブル ・・・ グローバルマクロ変数を入れる箱

図1
つまりマクロ変数がローカルなのか?グローバルなのか?という問題は、どちらのシンボルテーブルに入るかを問題としているわけです。




CALL SYMPUT で定義したマクロ変数の、ローカル・グローバルの判定方法


📝 説明の前に・・
SAS社のリファレンスの説明と実際の動作に差異があったため、私の実験結果に基づく動作の説明も含みます。何か不足している点など、お気づきの点がありましたらコメントいただけると幸いです。



CALL SYMPUT が含まれるデータステップの「run;」がマクロの中にあるのか、マクロの外にあるのかで、ローカル・グローバルの判定結果が変わります。


マクロの中に「run;」がある
%macro TEST;
   data _null_;
      call symput("MAC1", "1234");
   run;
%mend;
%TEST;

マクロ外に「run;」がある
data _null_;
   call symput("MAC1", "1234");
run;



判定は以下のとおり。
図2



具体例


では具体例をみていきましょう。
まず今回の解説で使うマクロ変数を予めリセット(削除)しときます。

%symdel MAC1 MAC2 MAC3 MAC4 MAC5 MAC6 MAC7 MAC8 MAC9 MAC10 / nowarn;



① マクロ外で定義したマクロ変数が、「グローバル」になる例。

data _null_;
   call symput("MAC1","abcd");
run;
%put _user_;

ログ
GLOBAL MAC1 abcd


② マクロ内で定義したマクロ変数が、「グローバル」になる例。

%macro TEST;
    data _null_;
       call symput("MAC2","abcd");
    run;

    %put _user_;
%mend;
%TEST;

ログ
GLOBAL MAC2 abcd


③ マクロ内で定義したマクロ変数が、「ローカル」になる例。

%macro TEST;
    %let MAC3 = abcd;

    data _null_;
       call symput("MAC4","1234");
    run;

    %put _user_;
%mend;
%TEST;

ログ
TEST MAC3 abcd
TEST MAC4 1234



例外

上のほうで示した「図2」のオレンジ色で示した「例外あり」のルートでは以下の例外が起きます。

① マクロ内で「SQLプロシジャ」の後に実行するCALL SYMPUTのマクロ変数は「ローカル」になる。

%macro TEST;
    proc sql ;
       select * from SASHELP.CLASS;
    quit;

    data _null_;
       call symput("MAC5","abcd");
    run;

    %put _user_;
%mend;
%TEST;

ログ
TEST MAC5 abcd


② マクロ内で「%GOTO &マクロ変数」や「%GOTO %マクロ名」みたいな記述があると、その後のCALL SYMPUTのマクロ変数は「ローカル」になる。

%let MAC6=GOGO;

%macro TEST;
    %goto &MAC6;
    %GOGO:

    data _null_;
       call symput("MAC7","1234");
    run;

    %put _user_;
%mend;
%TEST;

ログ
TEST MAC7 1234
GLOBAL MAC6 GOGO



③ マクロで「parmbuff」を使っていると、CALL SYMPUTのマクロ変数は「ローカル」になる。

%macro TEST / parmbuff;
    data _null_;
       call symput("MAC8","1234");
    run;

    %put _user_;
%mend;
%TEST;

ログ
TEST MAC8 1234



失敗例


① たまに質問を頂くので注意喚起。
CALL SYMPUTで定義したマクロ変数を同じデータステップ内で参照することは出来ません。

data _null_;
   call symput("MAC9","123");
   x = &MAC9;
run;

ログ
 WARNING: MAC9のシンボリック参照を解決できません。




② これは上級者が遭遇する失敗例。
「CALL SYMPUTでマクロ変数を定義」&「そのマクロ変数を使用(参照)する」みたいなマクロがあったとして、そのマクロをCALL EXECUTEで実行すると、うまくマクロ変数の参照が出来ないようです。

%macro TEST;
   data _null_;
      call symput("MAC10","123");
   run;

   %put &MAC10;
%mend;

data _null_;
   call execute('%TEST');
run;

ログ
 WARNING: MAC10のシンボリック参照を解決できません。

SAS社のUsage Noteにも載ってました。



てことで、どうでしょう、、正直最初はなんじゃこりゃーって感じでいろんなシチュエーションがあって覚えらんないですよね。慣れれば、なんとなくわかってきます。




記事一覧

1. 基本概念
2. %LET編
3. CALL SYMPUT編
4. マクロ変数の展開
5. マクロ変数の作成場所を明示的に指定する

2017年7月25日火曜日

そのマクロ変数、ローカル?グローバル?②%LET編




%LETでマクロ変数を定義した時に、ローカル・グローバルどちらになるのか説明します。
まずは以下がSAS内部の判定方法なのでご覧ください。






上の図を整理すると、、マクロ変数を定義する場所によって以下の動作をします。

マクロの外
・「グローバル」になる

マクロの中
・まず、同名のマクロ変数が既に定義されていないかチェックする。
・チェックする順は、「ローカル」→「グローバル」の順
・既に定義済の場合は、上書きする。
・「ローカル」にも「グローバル」にも定義済じゃない場合は、「ローカル」に新規作成。



では具体例でみていきましょう。
まず今回の解説で使うマクロ変数を予めリセット(削除)しときます。

%symdel MAC1 MAC2 MAC3 / nowarn;





動作確認


① マクロ外で定義したマクロ変数が、「グローバル」になる例。

%let MAC1 = abcd;
%put _user_;

ログ
GLOBAL MAC1 abcd



② マクロ内で定義したマクロ変数が、「ローカル」になる例。

%macro TEST(  MAC2 );
       %let MAC2 = 1234;
       %put _user_;
%mend;

%TEST( abcd );

ログ
TEST MAC2 1234

1つ補足ですが、マクロの引数として定義したマクロ変数は、必ず「ローカル」になります。
つまり、「%macro TEST(  MAC2 )」でローカルマクロ変数「MAC2」を定義しています。

次に「%let MAC2 = 1234」で、上記で既に定義した同名のローカルマクロ変数を上書きしています。



③ マクロ内で定義したマクロ変数が「グローバル」になる例

%let MAC1 = abcd;

%macro TEST;
   %let MAC1 = 1234;
   %put _user_;
%mend;

%TEST;

ログ
GLOBAL MAC1 1234

同名のグローバルマクロ変数が既に定義済なので上書きします。



④ マクロ内で定義したマクロ変数が、「ローカル」になる例。

%macro TEST;
       %let MAC3 = abcd;
       %put _user_;
%mend;

%TEST;

ログ
TEST MAC3 abcd

同名のマクロ変数がローカルにもグローバルにも定義済じゃないので、ローカルマクロ変数が作成されます。




次回はCALL SYMPUTによるローカル・グローバルの判定方法を紹介します。



記事一覧

1. 基本概念
2. %LET編
3. CALL SYMPUT編
4. マクロ変数の展開
5. マクロ変数の作成場所を明示的に指定する

2017年7月18日火曜日

そのマクロ変数、ローカル?グローバル?①基本概念




マクロ変数には、以下2種類あるのをご存知でしょうか?

 ローカルマクロ変数


 定義したマクロ内でしか参照できないマクロ変数
 (そのマクロが実行終了すると消去される)

 グローバルマクロ変数 
 
 
 どの場所からでも参照できるマクロ変数
 (ただし、CARDS, DATALINESステートメント内では参照不可




では具体例を見ていきます。
(ただし、どうしてこれがローカルになるの?グローバルになるの?っていうのは別記事で紹介予定なので、今は気にしないでください。)




ローカルマクロ変数の例


以下マクロ「TEST」の中で、ローカルマクロ変数「X」を定義しています。

%macro  TEST;
     %let  X = abcd;
%mend;

%TEST;


ここで以下赤字の%putステートメントを用いて、
マクロ「TEST」の中で、マクロ変数 X の値を展開してみます。

%macro  TEST;
     %let  X = abcd;
     %put  &X;
%mend;

%TEST;

ログ
abcd


問題なくマクロ変数 X の値が展開できている事が分かります。
では次に、マクロの外で展開しようとすると、、

%macro  TEST;
     %let  X = abcd;
%mend;

%TEST;
%put  &X;

ログ
WARNING: Xのシンボリック参照を解決できません。

怒りのWARNINGが出てしまいます。
マクロ内で定義したローカルマクロ変数は、マクロの実行中にしか存在しないので、マクロ外からの展開は出来ません。




グローバルマクロ変数の例


以下では、グローバルマクロ変数「Y」を定義しています。
どこからでも展開することができます。

%let  Y = xyz;
%put  &Y;

%macro  TEST;
     %put  &Y;
%mend;
%TEST;

ログ
xyz
xyz




マクロ変数の表示。


マクロ変数がローカルとグローバルどちらで格納されているのか判断がつかない場合の対処法。

以下 「%put _user_;」で調べる事が出来ます。

%let  Y = xyz;

%macro  TEST;
     %let  X = abcd;
     %put  _user_;
%mend;
%TEST;

ログ
 TEST X abcd
 GLOBAL Y xyz

実際のログには余計なマクロ変数の値が出ますが、上の例ではそれらを除いて分かり易くしてます。
ログの結果から以下が分かります。

・マクロ「TEST」のローカルマクロ変数「X」 には "abcd" という値が格納されている
・グローバルマクロ変数「Y」には "xyz" という値が格納されている


_USER_ 以外にも以下の指定が可能です。

_AUTOMATIC_ … 自動マクロ変数を表示
_GLOBAL_       … グローバルマクロ変数を表示(自動マクロ変数は含まず)
_LOCAL_          ... ローカルマクロ変数を表示(自動マクロ変数は含まず)
_ALL_               … 全てのマクロ変数を表示(自動マクロ変数を含む)
_USER_            … 全てのマクロ変数を表示(自動マクロ変数は含まず)

2016年8月17日水曜日

マクロ言語入門12:補足




マクロ言語入門は今回で最終回になります。
あとはよく使う機能として以下の記事に目を通して頂くとバッチリです。

・ 値をマクロ変数に格納する「CALL SYMPUTX」その1
・ データステップ外で関数を使えるようにする「%SYSFUNC」その1



最後に
マクロはすごく便利ですが、使い方には注意が必要です。
例えば、以下のようなプログラムは、可読性が落ちてしまうかもしれません。

・ 「マクロ引数がいっぱいあるけど、どういう引数なのかコメント等による説明がない」
・ 「マクロをネストしまくっている」
・ 「%IFで何回も条件分岐している」




2016年8月10日水曜日

マクロ言語入門11:演算評価 【%EVAL、%SYSEVALF】



%EVAL関数」と「%SYSEVALF関数」を紹介していきます。





以下のプログラムと実行ログをご覧ください。

%put  10 + 200 ;

ログ
10 + 200

 「10 + 200」 を計算して 「210」 とログに出力されることを期待しましたが、実際は文字がそのまま返されています。
このような場合、%EVAL関数や%SYSEVALF関数が使えます。




構文

  %EVAL(   演算内容   )

  • 中に書かれた演算を実行する。ただし整数のみ。
  • 割り算等で結果が小数になる場合は、小数部分が破棄されてしまう。
  • 「2 = 2.0」や「a < b」のように値に小数や文字を含む場合、文字値として評価を行います。
    (つまり「2」と「2.0」は異なる文字値として評価される)





  %SYSEVALF(   演算内容   )

  • 中に書かれた演算を実行する。浮動小数点の演算が可能。
  • 浮動小数点の演算後、BEST32形式に変換された値が返される。







%EVAL関数の例
%put %eval( 10 + 200 ) ;
%put %eval( 1 < 2 ) ;
%put %eval( 2 = 2.0 ) ;

ログ
210
1
0

補足 ・・・ 「1 < 2」 のような論理式の場合、論理式が真(true) の場合 「1」が返され、偽(false) の場合 「0」が返されます。



%SYSEVALF関数の例
%put %sysevalf( 0.1 + 0.2 ) ;
%put %sysevalf( 2 = 2.0 ) ;

ログ
0.3
1





記事一覧

1. マクロ変数とは
2. マクロの登録と実行
3. 定位置パラメータ
4. キーワードパラメータ
5. クォート処理
6. クォート処理2
7. ループ処理
8. 条件分岐処理
9. ドット
10. &&
11. 演算評価
12. 補足



2016年7月27日水曜日

マクロ言語入門10:マクロ変数と&&



「&&マクロ変数」という指定方法を紹介します。





例1

%let  VAR1 = abcde;
%let  X = 1;

%put  &&VAR&X ;

ログ
abcde



「&&」と書くと、「&」を1つに置き換える命令になります。
この機能の何が便利なのか解説していきます。


  • もし「&VAR&X」と書いた場合「&VAR」「&X」という2つのマクロ変数が指定されていると解釈されますが、
  • 「&&VAR&X」とすると初手では「&&」「VAR」「&X」という3つに分けて解釈されて、以下のように展開されます。
    • 「&&」→「&
    • 「VAR」→「VAR
    • 「&X」→「1
    • この3つの展開結果をくっつけると「&VAR1」になって、これがさらに展開されて「abcde」が返されます。


初手で「VAR」の部分がマクロ変数として解釈されないようにするテクニックです。




例2

%let  VAR1 = aaa;
%let  VAR2 = bbb;
%let  VAR3 = ccc;

%macro TEST;
    %do i = 1 %to 3;
          %put  &&VAR&i ;
    %end;
%mend;

%TEST;

ログ
aaa
bbb
ccc

%DOループと&&マクロ変数の組み合わせはよく用います。




実践例

以下に&&の実践的な使い方の記事リンクを貼っておきます。
慣れるまでややこしい感じのプログラムなので、余裕があったらそのうち、、という感じで参考にして頂ければと思います。

変数値をマクロ変数に格納する方法「CALL SYMPUTX編」




記事一覧

1. マクロ変数とは
2. マクロの登録と実行
3. 定位置パラメータ
4. キーワードパラメータ
5. クォート処理
6. クォート処理2
7. ループ処理
8. 条件分岐処理
9. ドット
10. &&
11. 演算評価
12. 補足


2016年7月26日火曜日

マクロ言語入門9:マクロ変数とドット




まず今後マクロを使ううえで便利な%PUTステートメントについて紹介します。
以下構文の通り、%PUTステートメントで指定した文字列をログに表示する事が出来ます。



構文

 %PUT   文字列;



例1
%let X = ABCDE;
%put  &X;

ログ
ABCDE

マクロ変数Xの値をログに出力しています。



例2
%let X = ABCDE;
%put  マクロ変数 X に "&X" を指定しました。;

ログ
マクロ変数 X に "ABCDE" を指定しました。

文字列とマクロ変数を組み合わせてログに出力しています。




ドットの活用



以下は例2と同じようなプログラムですが、WARNINGが出てしまいます。

%let X = ABCDE;
%put  マクロ変数 X に&Xを指定しました。;

ログ
WARNING: Xを指定しました。のシンボリック参照を解決できません。


これは 「&Xを指定しました。」 という名前のマクロ変数があるのだと解釈してしまったため、「そんな名前のマクロ変数存在しないぞ!」 というWARNINGです。

「&X」 と 「を指定しました。 」というように分けて解釈させたい場合、マクロ変数名の後に ドット「.」を入れてあげます。


%let X = ABCDE;
%put  マクロ変数 X に&X.を指定しました。;

ログ
マクロ変数 X にABCDEを指定しました。


マクロ変数名の後のドットは展開後に消えます。




ドットの活用2

%let LIB=WORK;
data &LIB.DT1;
   A=1;
run;

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


上記はデータセットDT1を保存するライブラリをマクロ変数LIBで指定するプログラムのつもりです。
しかしログを見ると実際できたデータセットは「WORK.WORKDT1」 となっています。


これは「&LIB.DT1」という文をSASが見たときに、「&LIB.」はマクロ変数の後にドットがついてるから、マクロ変数展開後にドットが消えて「WORKDT1」となってしまうからです。

以下のようにドットを2つ入れてあげると、展開後に1つだけドットが残ります。


%let LIB=WORK;
data &LIB..DT1;
   A=1;
run;
ログ
NOTE: データセットWORK.DT1は1オブザベーション、1変数です。




記事一覧

1. マクロ変数とは
2. マクロの登録と実行
3. 定位置パラメータ
4. キーワードパラメータ
5. クォート処理
6. クォート処理2
7. ループ処理
8. 条件分岐処理
9. ドット
10. &&
11. 演算評価
12. 補足




2016年7月6日水曜日

マクロ言語入門8:マクロ内での条件分岐処理【%IF】




マクロ内で「%IFステートメント」を使って、処理を条件によって分岐させる方法を紹介します




構文

%IF  条件式  %THEN  %DO;

          条件式がTRUEになった場合の処理

%END;
%ELSE %DO;

          条件式がFALSEになった場合の処理

%END;

  • %IFの条件式の判定法に注意! 例えば、
    • 「%IF 120>13」の条件は想定通り「TRUE」になりますが、
    • 「%IF 120.0>13.0」の条件は想定に反し「FALSE」になります
    • 理由は以下リンク記事で説明
    • https://sas-boubi.blogspot.com/2018/10/if.html
    • (SAS社の%IFのページにはこの事について触れていないので気づきにくい。。)




例1

%macro TEST( FLG );

   %if &FLG = Y %then %do;
       proc print data=SASHELP.CLASS;
       run;
   %end;

%mend;

%TEST();
%TEST(Y);


マクロ変数FLG に 「Y」 と入っていたらPRINTプロシジャが実行されます。
気をつけたいのがデータステップと勝手が違い、「%if &FLG = "Y"」というようにダブルクォーテーションで囲う必要はありません。

%IFステートメントではクォーテーションもテキストとして解釈されます。つまり「Y」という文字ではなく「"Y"」という文字として解釈されてしまいます。




例2

%macro TEST( FLG );

   data DT1;
       VAL1 = 1;
       %if &FLG = Y %then %do;
           VAL2=1;
       %end;
   run;

%mend;

%TEST();

 VAL1 
    1  


マクロ変数FLG に 「Y」 と入っていたら変数VAL2を作って「1」を代入するマクロです。
これ実はかなり便利です。なんでかというと、以下のように通常のIF文で同じようなことをやろうとすると、、


%let YN=;

data DT1;
    VAL1 = 1;
    if "&YN"="Y" then do;
        VAL2=1;
    end;
run;

 VAL1 
 VAL2  
    1      .


作りたくないVAL2が出来てしまいます。
これはたとえ実行されない文でもデータステップ内に書かれている変数はまず自動で変数の枠だけ作られてしまうという性質があるからです。

一方、%IFは通常のプログラムとは別のところで動くので、この動きを回避できちゃいます。




取り扱い注意


①「マクロ変数Xが欠損値だったら~」という%IFを書きたい場合

%if &X = %then %do;


少し違和感ありますがこれでOK(未確認ですが、昔のバージョンだとこの書き方ではERRORが出たらしい?)



② %IFが誤作動する例

%if &x = eq %then %do;


マクロ変数Xの値が「eq」かどうかを条件にしていますが、「eq」は「=」を意味しているので、演算子として解釈されて誤作動を起こして正しい結果が得られません。


他に誤作動を起こす文字は多数ありますが、それらは以下リンク記事で解説しているような「クォート処理」をする必要があります。


以下、適当にクォート処理を入れてみた例ですが、マクロ変数に格納される値によってはまだ誤作動を起こす危険があります。

%if %bquote(&x) = %str(eq) %then %do;

状況にあわせてどのクォート関数を使うのか、どのタイミングでクォート処理をするのかを判断する必要があります!




IF と %IF の大きな違い


  •  IFはデータステップ内で実行するステートメント
  •  %IFはデータステップ内とか外とかそういう概念がありません。


つまり%IFでは以下のようにデータセットの中にある変数の値を評価することは出来ません。

X 勘違い注意 X
%macro TEST;

data DT1;
    A=1;
    %if A=1 %then %do;
        B=1;
    %end;
run;

%mend;
%TEST;

%IFでは上記のプログラムを「A」という文字列と「1」という文字列がイコールかどうかを評価しちゃいます。
当然イコールじゃないので中の 「B=1;」 は実行されません。




2016年6月2日木曜日

マクロ言語入門7:マクロ内でのループ処理【%DO】



マクロ内でループ処理をする「%DO-%ENDステートメント」を紹介します。





構文

%DO  マクロ変数名 = 開始値  %TO  終了値;

           繰り返し実行したい処理

%END;

  • [マクロ変数] の値を [開始値] から [終了値] まで変化させながら、中に書かれた処理を繰り返し実行します
  • マクロの中でのみ利用可能




%macro TEST;

   %do no = 1 %to 5;
        data DT&no ;
        run;
   %end;

%mend;
%TEST;



マクロ変数NOの値を1から5まで変化させながら、中に書かれた処理を繰り返し実行します。
つまり以下のプログラムが実行される事になります。

data DT1;
run;

data DT2;
run;

data DT3;
run;

data DT4;
run;

data DT5;
run;

2016年5月31日火曜日

【%LET Y=%X】 みたいに書いて指定変数の統計量をマクロ変数に直接格納する方法





SASプログラミング掲示板で過去に以下の質問がありました。
http://tumesas.progoo.com/bbs/tumesas_topic_pr_20.html (サイト閉鎖されたためリンク切れ)


%let Y = %X(データセット, 対象変数 );


みたいな書き方で、「マクロ変数Yに統計量などを格納する事は出来ないか?」 という質問です。
この時、FCMPを使う方法を提案したんですが、もっといい方法を思いついたので、また自己満足のために書き留めておきます。



着想としては以下の記事あたり。
マクロで 【%LET Y=%X】 みたいな書き方
データステップ内でプロシジャを実行する。


以下は、SUMMARYプロシジャを実行して、好きな統計量を直接マクロ変数に落とすマクロです。

*** マクロ登録 ;
%macro m_summary( ds, var, stat );
/********************************************************************
   ds       … 対象データセット
   var      … 対象変数
   stat     … 統計量キーワード
********************************************************************/


/* dosubl関数で値を設定するマクロ変数_mputが既に存在していたら削除 */
/* (削除しないと_mputに正しい値が入らない事があったため) */
%symdel _mput / nowarn;


%local _rc _mput;
%let _rc = %sysfunc(dosubl(%str(


       /* SUMMARYプロシジャを実行 */
       proc summary data=&DS;
          output out=OUT1 &stat(&var)=_mvar1;
       run;

       /* 結果をマクロ変数に格納 */
       /* マクロ変数に数値をいれると、元の値と誤差がでる事があるので注意 */
       data _null_;
          set OUT1;
          call symputx('_mput', _mvar1);
       run;
)));

&_mput

%mend;


*** マクロを実行してみる ;
%let val=%m_summary( sashelp.class, age, mean );
%put &val;


*** データステップでも実行出来るようになってます ;
data DT1;
 VAL1 = %m_summary( sashelp.class, age   , mean );
 VAL2 = %m_summary( sashelp.class, height, mean );
run;



💬 注意事項
プログラム中に青字で書いてるように、dosubl関数内ではマクロ変数が正しく作られない場合があるので、実行後正しい結果が返ってくるか、テストが必要です


また「データステップ100万回」にて紹介されている通り、
マクロ変数に数値をいれると、元の値と誤差がでる事があるので注意。

マクロ変数に数値をいれて戻すと誤差がでちゃう場合がある問題について考える話



実用的かどうかは置いといて、個人的に満足したので良しとします。。
(アイディアの段階のため、この方法を利用する際は、上で記載した問題を考慮する必要があるのと、動作テストも行ったほうが良いです)



2016年5月26日木曜日

マクロ言語入門6:クォート処理【%BQUOTE関数】




%STR と %BQUOTE は同じクォート処理系の関数ですが、まったく異なる動きをします。



%STR関数の性質

%STRで囲った範囲の特殊文字をクォートする。



%BQUOTE関数の性質

%BQUOTEで囲った範囲に「クォート処理されていないマクロやマクロ変数」が含まれる場合は可能な限り展開してから、結果に含まれる特殊文字をクォートする。




具体例


以下は、変数Aに値を入れるマクロです。

%macro TEST( VAL );

       data DT1;
           length A $30.;
           A = "&VAL";
       run;

%mend;


このマクロを実行して変数Aに「A,B,C」という値を入れたいとします。

 
 A,B,C  


まどろっこしい書き方ですが、以下のように書いて実行すると、、

%let X= A,B,C;
%TEST( &X );

ログ
ERROR: マクロに定義された数よりも多い定位置パラメータが与えられています。

エラーが出ます。これは 「%TEST( &X );」 → 「%TEST( A,B,C );」という順に展開されるわけですが、
「%TEST(A,B,C)」にはカンマが含まれてるので「3つ引数があるのか」と解釈されてしまうからです。




%BQUOTE関数を使ってみる


マクロ変数Xを展開した後の値「A,B,C」をクォート処理したいので、%BQUOTE関数を使います。

%let X = A,B,C;
%TEST( %bquote(&X) );

 
 A,B,C  




%BQUOTE , %NRBQUOTE関数


%BQUOTE と %NRBQUOTE は同じ性質ですが、クォート対象の文字が異なります。

マクロ関数対象の文字
%BQUOTE






+ - * / < > = ¬ ^ ~ ; , # 半角スペース
AND OR NOT EQ NE LE LT GE GT IN ' " ( )

以下のようにクォートする文字としてカッコを含めて記述すると、SASが混乱してバグってしまうので注意
%bquote(aa(bb)
%bquote(aa)bb)
%NRBQUOTE




%BQUOTE関数と同じ+ & %

このマクロ関数は挙動を勘違いしやすいうえ、扱いづらいため個人的にはあまり使ってません。
以下「%NRBQUOTEの詳しい挙動」も要確認!



💬 %NRBQUOTEの詳しい挙動

例えば、以下2つのマクロ変数があったとします。
  • マクロ変数X: 「&Y」という値がクォート処理されずに格納されている
  • マクロ変数Y: 「&Z」という値がクォート処理されずに格納されている


これに対して、以下を実行すると、、

%put %nrbquote(&X);

%BQUOTE, %NRBQUOTEで囲った範囲の「クォート処理されていないマクロやマクロ変数」は可能な限り展開されるので、

→「&X」を展開すると「&Y」
→「&Y」を展開すると「&Z」
→「&Z」というマクロ変数は存在しないため、これ以上展開できない
→「&Z」を展開できない旨のWARNINGをログに出力


%NRBQUOTEは「&」もクォート対象になっているので、

→「&Z」というマクロ変数として解釈されないようクォート処理




実践例


%SYSFUNC関数を使う時によくクォート処理します。詳細は以下過去記事をご覧ください。



とにかくマクロ関数は複雑で、何故そうなるの?という挙動が沢山あります。
実際に使用する際は、結果をよく確認したほうが良さそうです。


2016年5月19日木曜日

マクロ言語入門5:クォート処理【%STR関数】





クォート処理とは?



以下は、変数Aに値を入れるマクロです。

%macro TEST( VAL );

       data DT1;
           length A $30.;
           A = "&VAL";
       run;

%mend;


このマクロを実行して変数Aに「A,B,C」という値を入れたいとします。

 
 A,B,C  


何も考えず書くと、、

%TEST(A,B,C);

ログ
ERROR: マクロに定義された数よりも多い定位置パラメータが与えられています。

エラーが出ます。これは%TEST(A,B,C)にカンマが含まれてるので「3つ引数があるのか」と解釈されてしまうからです。



解決策
%STR関数で囲ってあげます。

%TEST( %str(A,B,C) );

 
 A,B,C  


%STR関数は、カンマやセミコロンなどSASの構文として意味のある特定の文字を、ただの文字として解釈させる関数です。
このただの文字として解釈させる事を「クォート処理」といいます。





%STR, %NRSTR関数



関数によってクォート対象の文字が異なります。

マクロ関数対象の文字備考
%STR




+ - * / < > = ¬ ^ ~ ; , # 半角スペース
AND OR NOT EQ NE LE LT GE GT IN



' " ( )
上記文字をクォートしたい場合、その文字の前に%をつける(%直後の1文字のみに効果がある)
この方法の落とし穴を記事の下の方の例③に示しているので要確認!
%NRSTR

%STR関数と同じ+ & %

%STR関数と同じ



ちなみに%NRSTR の先頭の「NR」は 「Not Resolved」の意味で、つまり「マクロやマクロ変数を展開させない」という意味合いがあります。
なので&や%がクォート対象となっています。



では最初に書いたマクロTESTを使って、クォート処理の例を紹介します。
(例③④は落とし穴的な挙動なので要注意です!)






例①
欲しい結果
 
 Mr.&Mrs.Smith  

「&Mrs」 というマクロ変数として解釈されないようクォート処理をします。
%TEST( %nrstr(Mr.&Mrs.Smith);




例②
欲しい結果
 
 It's a small world  

シングルクォーテーションをクォート処理します。
%TEST( %str(It%'s a small world);



例③
欲しい結果
 
 100%JUICE  

「%JUICE」 というマクロとして解釈されないようクォート処理をします。
%TEST( %nrstr(100%JUICE);
   または
%TEST( %nrstr(100%%)JUICE );

2個目の例を「%TEST( %nrstr(100%)JUICE )」とすると、、

%NRSTR関数での「%)」は「)」をクォート処理せよという命令になってしまいます(%NRSTR関数の閉じカッコがクォート処理されるのでSASの挙動もおかしくなる)

そこで%STR, %NRSTR関数内に「%%」と書くと、
  • この「%」は直後の文字「'」「"」「(」「)」をクォートするという意味ではない、と命令する
  • 「%%」は「%」に置き換わる



例④
欲しい結果
 
 /* abcd */  

コメントステートメントとして解釈されないようクォート処理をします。
%TEST( %str(/)%str(*) abcd %str(*)%str(/) );

「%TEST( %str(/* abcd */) )」という書き方はNG。%STR関数の中でもコメントステートメントとして認識されるからです。なので「/*」を1文字ずつ%STR関数で分けてクォート処理しています。

ちなみに、コメントステートメントの種類(「/* */」「* ;」「%* ;」)によってマクロ内での挙動が異なっているのが影響してますが、
ほしい結果が「* abcd ;」の場合は「%TEST( * abcd ; )」という書き方でOK。
ややこしい。。コメントステートメントを含む文字をクォート処理する場合は挙動確認したほうが良いです。




(おまけ) シングルクォートとダブルクォート


以下プログラムは、&Mrsというマクロ変数が指定されてると解釈されてしまい、ログにWARNINGが出ます。

data OUT1;
  A = "Mr.&Mrs.Smith";
run;

ログ
WARNING: MRSのシンボリック参照を解決できません。


しかし、以下はWARNINGが出ず期待通りの結果が得られます。

data OUT2;
  A = 'Mr.&Mrs.Smith';
run;


これは「シングルクォーテーションで囲まれた文字に&や%が含まれていても、ただの文字として解釈される」というルールがあるからです。

2016年5月13日金曜日

マクロ言語入門4:パラメータの設定【キーワードパラメータ】



マクロに「キーワードパラメータ」というものを設定する方法を紹介します。





基本構文


マクロの登録
%MACRO  マクロ名( マクロ変数1=, マクロ変数2= ・・・ );

   ~プログラム~

%MEND;

  • 同名のマクロを同じ場所(カタログ)に複数作成することはできないので注意(上書きされる)
  • まず「%MACRO;」~「%MEND;」の中にマクロとして登録したいプログラムを書きます。
  • マクロ内で使用したいマクロ変数を「マクロ名」のカッコ内に列挙しておきます。




上記「マクロ名」のカッコ内に列挙したマクロ変数に対し、以下のように値を設定してマクロを実行します。

マクロの実行
%マクロ名( マクロ変数1=値, マクロ変数2=値 ・・・ )

  • マクロ変数に設定する値の両端に半角スペースを入れても、それらスペースはマクロ変数に含められません(別記事で解説する%STR関数を使用して含めることも可)
  • また、実行時「このマクロ名はNG」「このステートメントはマクロではNG」などのマクロのルールに違反している旨のログメッセージがないかも要確認です。




簡単な例

*** マクロ登録 ;
%macro TEST( DS= );
     proc print data=&DS;
     run;
%mend;

*** マクロ実行 ;
%TEST( DS=SASHELP.CLASS )
;


最終的に以下のプログラムが展開・実行されます。
proc print data=SASHELP.CLASS;
run;




パラメータに初期値を与える

*** マクロ登録 ;
%macro TEST( DS=SASHELP.CLASS );
     proc print data=&DS;
     run;
%mend;

*** マクロ実行 ;

%TEST();
%TEST(DS=SASHELP.IRIS);
%TEST(DS=SASHELP.RETAIL);



%macro TEST( DS=SASHELP.CLASS )」 で TESTマクロのマクロ変数DSに SASHELP.CLASS という初期値を与えています。


よってこのマクロを実行する際、

  • %TEST()」 でマクロ変数DS への値を指定しないと初期値 SASHELP.CLASS が使われて実行。
  • %TEST(DS=SASHELP.IRIS)」 でマクロ変数DS の値を SASHELP.IRIS に上書きして実行。
  • %TEST(DS=SASHELP.RETAIL)」 でマクロ変数DS の値を SASHELP.RETAIL に上書きして実行。

という動きをします。




記事一覧

1. マクロ変数とは
2. マクロの登録と実行
3. 定位置パラメータ
4. キーワードパラメータ
5. クォート処理
6. クォート処理2
7. ループ処理
8. 条件分岐処理
9. ドット
10. &&
11. 演算評価
12. 補足


2016年5月12日木曜日

マクロ言語入門3:パラメータの設定【定位置パラメータ】



マクロに「定位置パラメータ」というものを設定する方法を紹介します。





基本構文



マクロの登録
%MACRO  マクロ名( マクロ変数1, マクロ変数2 ・・・ );

   ~プログラム~

%MEND;

  • 同名のマクロを同じ場所(カタログ)に複数作成することは出来ないので注意(上書きされる)
  • 「%MACRO」~「%MEND;」の中にマクロとして登録したいプログラムを書きます。
  • マクロ内で使用したいマクロ変数を「マクロ名」のカッコ内に列挙しておきます。




上記「マクロ名」のカッコ内に列挙したマクロ変数に対し、以下のように値を設定してマクロを実行します。

マクロの実行
%マクロ名( マクロ変数値1, マクロ変数値2 ・・・ )

  • マクロ変数値の両端に半角スペースを入れても、それらスペースはマクロ変数に含められません(別記事で解説する%STR関数を使用して含めることも可)
  • また、実行時「このマクロ名はNG」「このステートメントはマクロではNG」などのマクロのルールに違反している旨のログメッセージがないかも要確認です。






*** マクロ登録 ;
%macro TEST( DS );
     proc print data=&DS;
     run;
%mend;

*** マクロ実行 ;
%TEST( SASHELP.CLASS )
;

  • %macro TEST( DS )」 で「TESTマクロにDSというマクロ変数があるよ」と教えておく。
  • %TEST( SASHELP.CLASS )」でマクロ変数DS に SASHELP.CLASS という値を当てはめて実行。


最終的に以下のプログラムが展開・実行されます。

proc print data=SASHELP.CLASS;
run;




記事一覧

1. マクロ変数とは
2. マクロの登録と実行
3. 定位置パラメータ
4. キーワードパラメータ
5. クォート処理
6. クォート処理2
7. ループ処理
8. 条件分岐処理
9. ドット
10. &&
11. 演算評価
12. 補足


2016年5月11日水曜日

マクロ言語入門2:マクロの登録と実行【%MACRO、%MEND】





データセットあるいは変数を変えて同じような処理を繰り返し実行したい場合、マクロを使うと便利です。
まずは基本構文から触れてみましょう。




基本構文



マクロの登録
%MACRO  マクロ名;

   ~プログラム~

%MEND;

  • 同名のマクロを同じ場所(カタログ)に複数作成することは出来ないので注意(上書きされる)
  • 「%MACRO」~「%MEND;」の中にマクロとして登録したいプログラムを書きます。



マクロの実行
%マクロ名

  • 登録したマクロを「%マクロ名」で実行する事が出来ます。
  • 実行時、「このマクロ名はNG」「このステートメントはマクロではNG」などのマクロのルールに違反している旨のログメッセージがないかも要確認です。




例①

*** マクロの登録 ;
%macro TEST;
   proc print data=SASHELP.CLASS;
   run;
%mend;

PRINTプロシジャでデータセットSASHELP.CLASSを出力するマクロTESTを登録しています。
このプログラムでマクロTESTは登録されますが、まだマクロの中身のプログラムは実行されません。



* マクロの実行 ;
%TEST;

マクロTESTを実行。
これにより先に登録しておいたマクロTESTの中身のプログラムが実行されます。


ちなみに、マクロの中にマクロを入れ込む事も出来ます。詳細は以下記事を参照ください。
マクロ実行文をログに展開するMPRINTとMPRINTNEST




例②

*** マクロ登録 ;
%macro TEST;
   proc print data=&DS;
   run;
%mend;

*** マクロ実行 ;
%let DS = SASHELP.CLASS;
%TEST;

%let DS = SASHELP.IRIS;
%TEST;

PRINTプロシジャでデータセットを出力するマクロTESTを登録。
「proc print data=&DS」 というように出力対象のデータセットを 「&DS」 とマクロ変数化しています。

これにより、以下のように%LETステートメントで出力するデータセットを変更しながらマクロを繰り返し実行することが出来ます。

%let DS = 出力対象のデータセット;
%TEST;