2015年6月29日月曜日

DS2プロシジャ入門4:データ型①


DS2プロシジャには沢山のデータ型が用意されています。
詳細は 「sas ds2 data types」 などでググって出てくる海外SAS社の解説ページをご覧ください。

ここでは一部紹介するに留めます。


とりあえず、、

文字変数は「CHAR」、数値変数は「DOUBLE」という型が、いままでSASで使ってた文字型・数値型と同等なので、まずこの2つを覚えましょう。

CHAR と DOUBLE を宣言する構文
 dcl  char(長さ ) 変数名 ;
 dcl  double       変数名 ;

ちなみに 「dcl double VAR1 VAR2 VAR3;」 というように複数の変数をいっぺんに定義することも出来ます。


クオーテーションには注意。

以下で、変数VAR1 に'aa' という値を入れたいとします。

proc ds2;
  data _DT1 (overwrite=yes);

      dcl char(10) VAR1;
      method init();
          VAR1="aa";
      end;
   
  enddata;
  run;
quit;

ログ
WARNING: 行 74: 参照変数 aa は宣言されていません。タイプ double
のグローバル変数として作成しています。

WARNINGが出てしまいます。
問題は、プログラム青字 「VAR1="aa"」 です。

実はDS2プロシジャでは、文字をダブルクオーテーションで囲むと、識別子とみなします。
(識別子?って感じですが、要はここでは変数名として見なされているって事です)

よって "aa" は変数aaと解釈されてしまい、「そんな変数ないんだけど、ちゃんと宣言しろ」と注意されてしまったわけです。

解決策は簡単で、シングルクオーテーションで囲めば値と見なしてくれます。

  VAR1='aa';


マクロとクオーテーション

以下で、変数VAR1 にマクロ変数の値 'aa' を入れたいとします。

%let MACVAR=aa;

proc ds2;
  data DT1 (overwrite=yes);

     dcl char(10) VAR1;
     method init();
         VAR1 = "&MACVAR";
     end;

  enddata;
  run;
quit;

ログ
WARNING: 行 76: 参照変数 aa は宣言されていません。タイプ double
のグローバル変数として作成しています。

プログラム青字部分の "&MACVAR" が展開され、"aa" となります。
ダブルクオーテーションで囲んでるため、やはり変数名として判断されWARNINGが出てしまいます。
かといって、 '&MACVAR' とシングルクオーテーションで囲ってしまうと「&MACVAR」という値として解釈されて、マクロ変数として展開してくれません。


そこで、%TSLITマクロ関数の出番。
このマクロ関数はシングルクオーテーションの付与と、マクロ変数の展開をしてくれます

  VAR1 = %tslit( &MACVAR );

2015年6月26日金曜日

データステップ内で、色々なデータセットのオブザベーション数を取得する


(注意!)
今回の方法で正しくobs数を取得できないケースもあるので、記事の最後に貼っているリンク記事も参照下さい。



まずは適当なサンプルデータを用意。

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

data DT2;
   do i=1 to 20;
      output;
   end;
run;

data DT3;
   A="aa";  output;
   A="bb";  output;
   A="cc";  output;
run;

DT1 は10obs、DT2 は20obsを持つデータセットです。



ここで、DT3を読み込むときに、DT1とDT2のobs数も取得したいとします。

* 各データセットのOBS数を取得 ;
data OUT1;

   set DT3;

   if 0 then set DT1(drop=_ALL_)  nobs= _OBS1;
   if 0 then set DT2(drop=_ALL_)  nobs= _OBS2;

   DT1_OBS = _OBS1;
   DT2_OBS = _OBS2;

run;

  A  
 DT1_OBS 
 DT2_OBS 
  aa 
  10  
  20 
  bb
  10  
  20 
  cc
  10  
  20 


解説

プログラム青字部分がポイントです。
if 0 then」 の 「0」 は 「false」 の意味になり、then以降の 「set DT1(drop=_ALL_) ・・・」 の部分は実行されません。
。。のはずですが、たとえ実行されなくてもデータセット名の指定があると、コンパイル時にそのデータセットの定義情報だけが読み込まれます。

この性質を利用し、
・まず 「if 0 then set DT1」 でDT1の定義情報だけを読み込みます。
・変数定義も読み込まれてしまうので 「DT1(drop=_ALL_)」 で読み込まれる全変数をdropする。
・「nobs = 適当な一時変数名」で、定義情報に格納されたobs数を一時変数に格納。
といった流れです。


※ 以下記事の通り、特殊な処理をしてる場合は、正しくobs数を取得できないのでご注意ください。
行削除の落とし穴


2015年6月23日火曜日

DS2プロシジャ入門3:変数属性と配列の定義



① 属性を付与する方法

変数を宣言するついでに、属性も定義する感じです。

構文
 dcl  データ型  変数名  having   label   'ラベル'
                                                   format  フォーマット
                                                   informat  インフォーマット ;


proc ds2;
   data DT1 (overwrite=yes);
       dcl double VAR1 having  label 'aaa'  format yymmdd10. ;
       method init();
            VAR1 = inputn('2015/06/23', 'yymmdd10');
       end;
   enddata;
   run;
quit;




② 配列の定義方法

一時配列(DS2プロシジャを終了したら残らない)を作る構文
 dcl  データ型  配列名 [要素の数] ;


proc ds2;
   data _NULL_;
       dcl double AR[2];
       method init();
            AR[1] = 10;
            AR[2] = 20;
            put AR[1]=;
            put AR[2]=;
       end;
   enddata;
   run;
quit;

ログ
AR[1]=10
AR[2]=20


通常の配列(DS2プロシジャを終了しても変数は残る)を作る構文
 vararray データ型  配列名 [要素の数]   変数1 変数2 ・・・;


proc ds2;
   data DT2 (overwrite=yes);
       vararray double AR[2] A B;
       method init();
            AR[1] = 10;
            AR[2] = 20;
       end;
   enddata;
   run;
quit;


注意:
「vararray double AR[1] double」みたいな感じで、変数名を指定する部分にデータ型を指定すると特殊な動き(想定外の挙動)をするため、ご注意ください(詳細はリファレンスなどを参照下さい)


DS2プロシジャ入門記事

1: 基本構文
: 変数の宣言
3: 変数属性と配列の定義

2015年6月22日月曜日

RETAINステートメント徹底入門





RETAINは、前のオブザベーションの値を保持してくれるステートメントです。



構文

RETAIN  値の保持機能を持たせる変数   初期値 ;

  • 値の保持機能を持たせる変数」… データセット内に存在する変数を指定しても、うまくREATINされません。新しい変数を作って、それを指定しましょう。
    • 値の保持機能を持たせたいのが文字変数の場合は、先にLENGTHステートメント等で長さの設定も忘れずに。
  • 初期値」… 省略化



とりあえず、具体例を見ていきましょう。



サンプルデータ

data DT1;
input A @@;
cards;
1 . 5 . . 10
;
 A  
    1 
    .  
    5  
    .  
    .  
  10  



例①

data OUT1;
   set DT1;
   retain B 0;
run;

  B   
   1 
   0  
   .  
   0  
   5  
   0  
   .  
   0  
   .  
   0  
 10  
   0  


変数Bに初期値「0」を与えて、最後のオブザベーションまでその値が保持され続けていることが確認できます。



例②

data OUT2;
   set DT1;
   retain A2;
   if A^=. then A2=A;
run;

  A2   
   1 
    1  
   .  
    1  
   5  
    5  
   .  
    5 
   .  
    5  
 10  
  10  


  • RETAINで変数A2に値保持機能を持たせます。
  • IF文で、変数Aに何か値があれば、その値をA2に放り込んでいます。


結果として、「変数Aに値がなければ、前のオブザベーションから値を引き継ぐ」という処理をした変数A2を作ることが出来ます。




実践例①


サンプルデータ
data DT2;
input NO$ DAY POINT$;
cards;
001 1 100pt
001 2 .
001 3 .
001 4 150pt
002 1 .
002 2 200pt
002 3 .
002 4 .
;
 NO  
 DAY 
 POINT 
  001
  1  
 100pt 
  001
  2  
  
  001
  3  
   
  001
  4  
 150pt
  002
  1  
  002
  2  
 200pt   
  002
  3  
   
  002
  4  
    

NO      ・・・ 顧客NO
DAY    ・・・ 来店日
POINT ・・・ ポイント


サンプルデータの説明。
とあるお店がポイントカードを導入したとします。
顧客NO毎にポイントを管理していて、来店してない日はポイントをNULLにしています。

(突っ込みどころ満載のデータですが、あくまで説明用のためのデータです。。)


ここで、来店してない日は、前回来店時のポイントで埋めたいとします。

 NO  
 DAY 
 POINT2 
  001
  1  
 100pt 
  001
  2  
 100pt
  001
  3  
 100pt
  001
  4  
 150pt
  002
  1  
  
  002
  2  
 200pt   
  002
  3  
 200pt 
  002
  4  
 200pt


*** 来店してない日を、前回来店時のポイントで埋める。 ;
proc sort data=DT2;
   by NO DAY;
run;

data OUT3;
   set  DT2;
   by  NO ;

   length  POINT2 $8.;
   retain  POINT2;
   if  first.NO then POINT2="";
   if  POINT ^="" then POINT2 = POINT;
run;

以下記事の方法も組み合わせてます。
「FIRST.BY変数」と「LAST.BY変数」で、グループ毎の最初と最後のオブザベーションを特定する。


  • まず「retain  POINT2」で、POINT2に値保持機能を持たせる。(この変数にポイント埋めをします)
  • if  first.NO then POINT2=""」で、RETAIN機能を有したPOINT2を、顧客NOごとに初期化(欠損値に)する。(初期化しないと、前の顧客のポイントがRETAINされちゃうので。)
  • if  POINT ^="" then POINT2 = POINT」で、POINTに何か値があれば、その値をRETAIN機能を有したPOINT2に放り込んでいます。

これで、ポイントを埋めた変数POINT2の完成です。



実践例②


サンプルデータ
data DT3;
input SUBJID$ VISITNUM VAL;
cards;
001 0 100
001 1 200
001 2 .
001 3 .
002 0 300
002 1 .
002 2 400
002 3 .
;
 SUBJID 
 VISITNUM 
 VAL 
  001
  0  
 100
  001
  1  
 200
  001
  2  
  . 
  001
  3  
  .
  002
  0  
 300
  002
  1  
  .   
  002
  2  
 400 
  002
  3 
  .

SUBJID          ・・・ 患者ID
VISITNUM    ・・・ 測定時期  0=投与前、1=投与1日後、2=投与2日後・・・
VAL                ・・・ なんかの測定値


サンプルは、治験薬投与前後にとある検査値を測定したデータだとします。

やりたい事は、測定値が欠損値であれば、以下のように前回測定された値を引っ張ってきたい。
(ただし、前回測定したのが投与前の場合は、投与後の値としては引っ張ってきたくない。)

 SUBJID 
 VISITNUM 
 VAL2 
  001
  0  
 100
  001
  1  
 200
  001
  2  
 200  
  001
  3  
 200
  002
  0  
 300
  002
  1  
  .   
  002
  2  
 400
  002
  3 
 400


* 前回測定値を引っ張ってくる(投与前以外) ;
proc sort data=DT3;
   by SUBJID VISITNUM ;
run;

data OUT4;
   set DT3;
   by SUBJID;

   length VAL2 8.;
   retain VAL2;

   if first.SUBJID then VAL2=.;
   if VAL^=. then VAL2=VAL;

   output;
   if VISITNUM=0 then VAL2=.;
run;


やってる事は、ほぼ「実践例①」と同じ。

ただし投与前の測定値は、投与後にRETAINしたくないので、
特定条件の時だけ、RETAIN機能を無効にする
の方法を組み合わせてます。(プログラム青字部分)


📝注意
記事の中で使用している「FIRST.BY変数」は「サブセット化IF」と一緒に使用すると正しく動かなくなりやすいです。



2015年6月13日土曜日

特定条件の時だけ、RETAIN機能を無効にする。




以下のデータを見てください。

*** サンプルデータ ;
data DT1;
input A B @@;
cards;
1 100 2 . 3 200 4 . 5 400 6 .
;

 A 
   1 
 100  
   2  
  . 
   3  
 200 
   4  
  .
   5  
 400
   6 
  .



このデータで、まずはBの値を単純にRETAINした変数B2を作ってみます。

*** Bの値をRETAIN ;
data OUT1;
   set DT1;

   retain B2;
   if B ^=. then B2 = B;
run;

 A 
B2
   1 
 100  
 100  
   2  
  . 
 100  
   3  
 200 
 200  
   4  
  .
 200  
   5  
 400
 400  
   6 
  .
 400  



次が問題。
たまに特定条件の時だけ、RETAIN機能を無効にしたい事があります。

たとえば、以下のように A=3 の時だけ、RETAINしたくないとします。
(3オブザベーション目の 「200」 が次のオブザベーション以降に保持されないようにする)

 A 
B2 
   1 
 100  
 100  
   2  
  . 
 100  
   3  
 200 
 200  
   4  
  .
   .  
   5  
 400
 400  
   6 
  .
 400  



*** A=3の時だけ、RETAIN機能を無効にする ;
data OUT2;
   set DT1;

   retain B2;
   if B ^=. then B2 = B;
   output;

   if A=3 then B2=. ;
run;



上の赤文字が追加した文。

データセットDT1の3オブザベーション目に対する処理の動きを説明すると、

①「output;」で3オブザベーション目の現状の処理結果をデータセットに出力してから、
②「if A=3 then B2=.;」で A=3 の場合にB2を初期化(欠損値に)しています。


上記①で3オブザベーション目に対する処理結果をデータセットに出力済みなので、②の処理は3オブザベーション目の処理としてはデータセットに反映されず、PDV (処理が行われる一時的な箱) のみに保持されています。

このPDV内のB2は、「retain B2;」により次のオブザベーションを読み込む際にもその値が引き継がれます。


③ 上記②でPDVのB2が初期化された場合、次のオブザベーションには欠損値になったB2が引き継がれる(=RETAIN機能が無効になる)、というわけです。



2015年6月9日火曜日

複数の変数から、欠損値以外の値を左にシフトしたい。



やりたい事

* サンプルデータ ;
data DT1;
input (SUBJID A B C D) ($);
cards;
001 .  bb .  aa
002 .  .  cc .
003 aa .  cc bb
004 . . .  .
;







上のデータで変数A, B, C, D のうち、非欠損値を左にシフトしたい。




では解決法。と、その前に前提条件
  • 左にシフトしたい変数はすべて同じ型であること。


解決法

data DT2;
   set DT1;
   length COL1-COL4 $5.;

   array AR1(*) A B C D;
   array AR2(*) COL1-COL4;

   NO = 0;

   do i=1 to dim(AR1);
       if not missing(AR1(i)) then do;
           NO + 1;
           AR2(NO) = AR1(i);
       end;
   end;
run;

概要
まず変数A, B, C, D を配列に入れる。
配列の値をループで順番に見ていき、もし非欠損値だったら新しい変数COL1~COL4へ順番に放り込んでいく。




なんか他にいい方法あったら教えてほしいです。


2015年6月8日月曜日

ファイル参照をいっぺんに割り当てる方法



ファイル参照をいっぺんに取り消す方法の逆で、割り当てる方法。


たとえば、「C:\TEST」というパスに 「A.txt」「B.txt」「C.txt」 というファイルがあったとします。
これらにファイル参照名を割り当てたいとします。



① FILENAMEステートメント


ファイル参照名を割り当てる構文
FILENAME ファイル参照名 "割り当てるファイルのフルパス";

* ファイル参照名の割り当て ;
filename myfile1 "C:\TEST\A.txt";
filename myfile2 "C:\TEST\B.txt";
filename myfile3 "C:\TEST\C.txt";


注意:
FILENAMEステートメントに存在しないファイルを指定した場合、その存在しないファイルにファイル参照名が割り当てられます。特にエラー等のメッセージも出ません。



② FILENAME関数


データステップ内でもFILENAME関数でファイル参照名の割り当てが出来ます。


ファイル参照名を割り当てる構文
リターンコードを格納する変数 = FILENAME( "ファイル参照名", "割り当てるファイルのフルパス" );

    • 割り当てが成功したか失敗したか、などの情報はログに表示してくれません。
    • その代わり、FILENAME関数のリターンコードには以下が格納される。
      • FILENAME関数の操作が成功した場合「0」
      • FILENAME関数の操作が何かしら成功しなかった場合は「0以外」(成功しなかった場合のメッセージはSYSMSGという関数で取得できる)
      • ただし「ファイル参照をいっぺんに取り消す方法」でも触れましたが、FILENAME関数のリターンコードが意図通りにならないケースがありました。なのであまりリターンコードは信用しないほうが良いかと個人的に思ってます。
    • FILENAMEステートメントと同様で、FILENAME関数に存在しないファイルを指定した場合、その存在しないファイルにファイル参照名が割り当てられます。特にエラー等のメッセージも出ません。


    * ファイル参照名を割り当てる ;
    data _null_;
            length msg $2000.;

            rc = filename( "myfile1", "C:\TEST\A.txt" );
            if rc^=0 then do;
               msg=sysmsg();
               put msg;
            end;
            rc = filename( "myfile2", "C:\TEST\B.txt" );
            if rc^=0 then do;
               msg=sysmsg();
               put msg;
            end;
            rc = filename( "myfile3", "C:\TEST\C.txt" );
            if rc^=0 then do;
               msg=sysmsg();
               put msg;
            end;
    run;



    以下のようにDOループなどを使っていっぺんに割り当てたり、色々工夫の余地があります。

    * ファイル参照名を割り当てる ;
    data _null_;
          length x msg $2000.;
          do  x = "A", "B", "C";
               i + 1;
               rc = filename( cats( "myfile", i ) , cats( "C:\TEST\", x, ".txt" ) );
               if rc^=0 then do;
                   msg=sysmsg();
                   put msg;
               end;
          end;
    run;


    ファイル参照をいっぺんに取り消す方法



    1つまたはすべてのファイル参照を取り消す方法を紹介します。



    ① FILENAMEステートメント


    ファイル参照を取り消す構文
    FILENAME ファイル参照名 CLEAR;

      すべてのファイル参照を取り消す構文
      FILENAME _ALL_ CLEAR;


        * 適当にファイル参照を割り当て ;
        filename test1 "適当なファイルパス1";
        filename test2 "適当なファイルパス2";

        * ファイル参照「TEST2」のみ取り消す ;
        filename test2 clear;




        ② FILENAME関数


        データステップ内でもFILENAME関数でファイル参照の取り消しが出来ます。


        ファイル参照を取り消す構文
        リターンコードを格納する変数 = FILENAME( "ファイル参照名" );

          • 取り消しが成功したか失敗したか、などの情報はログに表示してくれません。
          • その代わり、FILENAME関数のリターンコードには以下が格納される。
            • 取り消せた場合「0」
            • なんらかのERROR、WARNINGなど特記事項がある場合は「0以外」(特記事項はSYSMSGという関数で取得できる)
            • ただし、うまくいかないケースを見つけました(記事の最後に解説してます)


          * 適当にファイル参照を割り当て ;
          filename test1 "適当なファイルパス1";
          filename test2 "適当なファイルパス2";

          * ファイル参照「TEST1」「TEST2」を取り消す ;
          data _null_;
                  length msg $2000.;

                  rc = filename( "test1" );
                  if rc^=0 then do;
                     msg=sysmsg();
                     put msg;
                  end;

                  rc = filename( "test2" );
                  if rc^=0 then do;
                     msg=sysmsg();
                     put msg;
                  end;
          run;



          以下のようにDOループなどを使ってドカッと取り消したり、色々工夫の余地があります。

          * 適当にファイル参照を割り当て ;
          filename test1 "適当なファイルパス1";
          filename test2 "適当なファイルパス2";
          filename test3 "適当なファイルパス3";

          * ファイル参照「TEST1」~「TEST3」を取り消し ;
          data _null_;
                length msg $2000.;
                do i = 1 to 3;
                     rc = filename( cats( "test", i ) );
                     if rc^=0 then do;
                         msg=sysmsg();
                         put msg;
                     end;
                end;
          run;

          上の例を簡単に説明しておくと、
          • DOループで変数 i の値を1~3まで変化させながら、
          • CATS関数で "test" という文字と変数 i の値を結合します。
          • その結合した文字が表すファイル参照名をFILENAME関数で取り消しています。


          注意点

          「存在しないファイル参照名」を取り消そうとした場合、
          FILENAMEステートメントの場合は、WARNINGが出力されますが、

          filename abcd clear;

          ログ(ファイル参照名が存在しない場合):
          WARNING: ABCDが割り当てられていません。


          FILENAME関数で「存在しないファイル参照名」を取り消そうとすると、なぜかリターンコードに0が返されました(失敗扱いにならない)
          そのため、SYSMSG関数でWARNINGやERROR等のメッセージを取得することが出来ませんでした。


          ログにメッセージが出てくれないと困る、という場合はFILENAME関数ではなく、FILENAMEステートメントを用いる必要があります。


          2015年6月4日木曜日

          TRANSPOSEの2回実行テクニック。




          データをtransposeで2度漬けすると、1回のtransposeでは実現できないデータを作ることができます。
          その一例を紹介したいと思います。


          サンプルデータ

          data DT1;
              input NO$ SEQ  A$  B$  C$;
              cards;
          001 1 XX AA aa
          001 2 YY BB cc
          002 1 ZZ CC dd
          002 2 WW DD bb
          ;



          やりたい事

          変数NOの値が同じオブザベーションを、1つのオブザベーションにまとめたい。
          まとめるときの変数名は [元の変数名]_[SEQの値] とする。
          たとえば、SEQ=1の変数Aは「A_1」、SEQ=2の変数Aは「A_2」という具合です。






          📝
          前提条件として、転置する変数はすべて同じ型とします。型が異なる場合はこのテクニックは使えません(今回だと変数A, B, Cはすべて文字型)



          TRANSPOSE 1回目

          proc transpose data=DT1  out=DT2;
              var  A B C;
              by  NO SEQ;
          run;


          • まずは、変数NO, SEQ毎に、変数A, B, Cを全て縦に転置。
          • ちなみに、転置対象の変数(今回だとA, B, C)が
            • 全て同じ型で、全て同じフォーマットが割り当てられている場合・・・転置後もそのフォーマットが維持される。
            • 全て同じ型で、各変数に異なるフォーマットが割り当てられている場合・・・転置後はフォーマットが取り除かれる。

           

          転置対象の変数にフォーマットが割り当てられている場合は、転置する際に様々な挙動をするのでご注意ください (転置後にデータを確認すること!PROC CONTENTSを使って変数属性も確認してみるとよいです)



          TRANSPOSE 2回目

          proc transpose data=DT2  out=DT3  delim=_;
              var  COL1;
              by  NO;
              id  _NAME_ SEQ;
              format  SEQ;
          run;


          • by NO;」でNOの値ごとに転置。
          • id _NAME_ SEQ;」と「delim=_」の組み合わせで、転置したときの変数名を設定
            • 転置元のデータセットDT2の [_NAME_の値]_[SEQの値] となるようにしてます。
          •  ちなみに「TRANSPOSEのIDステートメント入門」で解説してますが、IDステートメントに指定した変数(今回だと_NAME_, SEQ)にフォーマットが割り当てられている場合は注意。フォーマットをあてたときの値が、転置後の変数名になってしまうからです。
            • 変数_NAME_はtranspose1回目の際にSASが勝手に作った変数で、この変数にはフォーマットは割り当てられない仕様なので問題なし。
            • 変数SEQはこちらで作った変数なので、もしフォーマットを割り当てていた場合は「format SEQ;」と記述して変数SEQのフォーマットを取り除いてから転置することが出来ます。あえてフォーマットをあてたときの値を、転置後の変数名にしたい場合は、この記述は書かなくてもよいので、時と場合によって記述するか判断する。


          ちなみに、transpose1回目の際にSASが勝手に「_NAME_」と場合によっては「_LABEL_」という変数を作りますが、これら変数は以下リンク記事にある通り注意が必要です。

          難点は2つあって、

          ひとつは、転置対象の変数がすべて同じ型であること。
          数値型と文字型が混在している場合は、転置する際にSAS側で色々と勝手な変換をしちゃうので、今回のテクニックには向いてません。

          もうひとつは、変数達のlengthが、一番大きいやつに合わせられちゃうところですかね。
          例えば、各変数のlengthが、以下だった場合、、
          A … $5
          B … $100
          C … $5

          転置された変数は、一番lengthの大きい$100に合わせられます。
          そのため、ファイル容量もデカくなりがちかも。


          メリット・デメリットあるけど、ダイナミックな動きをしてくれるので色んなケースで役に立ってくれます。


          2015年6月1日月曜日

          DS2プロシジャ入門2:変数の宣言


          今回は重要かつ細かい動きの解説になるので、間違いなどがあればご指摘お願いします。


          変数の宣言

          DS2プロシジャで変数を扱うには、まず変数の宣言が必要です。

          変数を宣言する構文。
           dcl  データ型  変数名 ;

          (データ型については別記事で解説予定なので、ここでは気にしないでください。)

          例 **********;
          proc ds2;
             data _DT1 (overwrite=yes);

                 dcl char(10) VAR1;

                 method run();
                     dcl char(10) VAR2;
                 end;
           
             enddata;
             run;
          quit;

          上の例では、長さ10の文字変数VAR1とVAR2を作成しています。

          また変数を宣言する場所によって、その変数の役割も異なってきます。


          ローカル変数

          メソッドの中で宣言した変数は、そのメソッド内でしか使用できないうえ、
          一時変数的な意味合いとなり、自動的に削除されます。

          以下の例を見てください。
          data DT1;
             do i=1 to 3;
                 output;
             end;
          run;

          proc ds2;
            data DT2 (overwrite=yes) ;
              method run();
                  dcl double A;
                  set DT1;
                  A + 1;
                  put A;
              end;
            enddata;
          run;
          quit;

          ログ
          1
          1
          1

          オブザベーションを読み込む度に「A+1」でどんどん足し合わせていこうとしてますが、
          出力結果のログを見ると、全然足されてません。

          これは、ローカル変数Aが一時的な変数のため、前回処理時の値を保持していないからです。
          よってRETAINやSUMステートメントの機能も無効になります。
          出力データセットにも変数は残りません。


          グローバル変数

          メソッドの外で宣言した変数は、各メソッドから参照する事ができます。
          (ローカル変数と違い、変数もちゃんと残ります)

          ここで気をつける事あり。以下を見てください。
          proc ds2;
            data DT1 (overwrite=yes) ;

              dcl double A;

              method init();
                  A=1;
                  put 'INITメソッドからPUT ・・・' A;
              end;
              method run();
                  put 'RUNメソッドからPUT ・・・' A;
              end;
              method term();
                  put 'TERMメソッドからPUT ・・・' A;
              end;
            enddata;
          run;
          quit;


          ログ
           INITメソッドからPUT ・・・ 1
           RUNメソッドからPUT ・・・ 1
           TERMメソッドからPUT ・・・ .

          やってる事
          ① まずグローバル変数Aを作ります。
          ② INITメソッド内で変数Aに値「1」を入れます。
          ③ そして各メソッドからAの値をログに出力してます。

          ログを見ると、TERMメソッドの結果だけ欠損値になってる!



          これは最後に実行されるTERMメソッドが、
          処理を終了するから、グローバル変数達の値はリセットしておくね」、、となるからです。

          (リセットされるグローバル変数は以下の場合を除く。)
          • SAS側があらかじめ定義している_N_などの変数
          • RETAINのような値を保持するようにしてる変数
          • パッケージ変数


          という事で、TERMメソッドには気を付けましょう!

          。。。ちなみに、出力データセットには、RUNメソッド実行時の値が格納されます。
          (TERMメソッド実行時の値を出力データセットに格納するなど、こちらで明示的に出力するタイミングを変えることも出来るっちゃ出来る)



          同名のグローバル変数とローカル変数を作った場合

          proc ds2;
             data _DT1 (overwrite=yes);

                 dcl char(10) VAR1;

                 method init();
                     dcl char(10) VAR1;
                     VAR1 = 'aaa';
                 end;
           
             enddata;
             run;
          quit;


          この場合INITメソッド内に書かれた 「VAR1 = 'aaa';」 は、INITメソッド内で宣言されたローカル変数VAR1に対しての処理となります。
          (出力データセットにはグローバル変数VAR1が出力されます)


          DS2プロシジャ入門記事

          1: 基本構文
          2: 変数の宣言