ラベル [▼ 変数名の取得 の投稿を表示しています。 すべての投稿を表示
ラベル [▼ 変数名の取得 の投稿を表示しています。 すべての投稿を表示

2014年9月4日木曜日

変数名を取得する方法 [まとめ]




変数名を取得する方法は沢山ありますが、知ってる方法すべてあげてみようと思います。

取得した変数名をマクロ変数に入れたい場合は
・変数値をマクロ変数に格納する方法「CALL SYMPUTX編」
・変数値をマクロ変数に格納する方法「SQL編」
などが参考になります。



data DT1;   * サンプルデータ ;
   label A="var1"  B="var2"  C="var3";
   A=1; B="AAA"; C=100; output;
   A=2; B="BBB"; C=200; output;
run;


*** ① CONTENTSプロシジャ ;
proc contents data=DT1 out=VARS1 noprint;
run;

一般的なのが、この変数定義情報を取得できるCONTENTSプロシジャ。



*** ② SQLのDICTIONARYテーブル ;
proc sql;
   create table VARS2 as
   select *
   from DICTIONARY.COLUMNS
   /* ライブラリ名, データセット名を大文字で指定 */
   where LIBNAME = "WORK" and MEMNAME = "DT1";
quit;

  • SQLプロシジャで参照することが出来るDICTIONARY.COLUMNS(全データセット, VIEWの変数定義情報が入ってる)を使う方法。
  • DICTIONARYテーブルの詳細は「こちら」を参照。


*** ③ OPEN, ATTRN, VARNAME関数 ;
data VARS3;
   length NAME $40.;
   DSID = open( "DT1" );
   VARN = attrn( DSID, "NVARS" );

   do i=1 to VARN;
       NAME = varname( DSID, i );
       output;
   end;
run;

  • まずOPEN関数でデータセットを開く。戻り値としてデータセット識別子というIDが返される。
  • 次に ATTRN( データセット識別子, "NVARS" ) で変数の数を取得。
  • 最後に変数の数だけループをまわして、VARNAME( データセット識別子, 変数の位置番号 ) で変数名を取得しています。



*** ④ CALL VNEXT ;
data VARS4;
   length _NAME $40.;
   if 0 then set DT1;

   do until ( _NAME="" );
       call vnext( _NAME );
       /* ^in の後のカッコの中は大文字で指定 */
       if upcase( _NAME ) ^in ( "_NAME", "_ERROR_", "_N_", "" ) then output;
   end;

   keep _NAME;
   stop;
run;

  • CALL VNEXTで変数名を取得。詳細は「こちら」を参照。
📝---------------
    • プログラム中の「_NAME」と同名の変数がSETするデータセットにもあると正しく動かなくなるので、その際は上記プログラム中の「_NAME」を別の名前に変えて下さい。


    *** ⑤ TRANSPOSE ;
    proc transpose data=DT1 (obs=0) out=VARS5;
       var _ALL_;
    run;

    海外のユーザー会で紹介された
    「Atypical Applications of Proc Transpose(John King, Ouachita Clinical Data Services Inc., Hopper, AR)」
    にある面白いテクニック。
    transpose本来の使い方とは異なり、面白いアイディアです。



    *** ⑥ ARRAY ;
    data VARS6;
       length _NAME $40. _ITER 8.;
       if 0 then set DT1;

       array AR1(*) _numeric_;
       do _ITER = 1 to dim( AR1 );
           _NAME = vname( AR1(_ITER) );

           /* ^= 直後のテキストは大文字で指定 */
           if upcase(_NAME) ^= "_ITER" then output;
       end;

       array AR2(*) _character_;
       do _ITER = 1 to dim( AR2 );
           _NAME = vname( AR2(_ITER) );

           /* ^= 直後のテキストは大文字で指定 */
           if upcase(_NAME) ^= "_NAME" then output;
       end;

       keep _NAME;
       stop;
    run;

    • ARRAYで全数値変数と全文字変数を配列に割り当てて、VNAME関数で変数名を取得してます。
    • VNAME関数は、変数名を文字として取得することができ、僕の頁「VNAME関数」で詳しく紹介されています。
    📝---------------
        • プログラム中の「_NAME」「_ITER」と同名の変数がSETするデータセットにもあると正しく動かなくなるので、その際は上記プログラム中の「_NAME」「_ITER」を別の名前に変えて下さい。


        2014年3月14日金曜日

        変数名を取得する「CALL VNEXT」。




        以下は、CALL VNEXTを使って変数定義をデータセットに書き出す例です。


        * サンプルデータ作成 ;
        data DT1;
        input A1 A2 A3;
        cards;
        1 1 2
        2 1 2
        3 1 2
        ;
        * 変数定義をデータセットに書き出す ;
        data DT2;

           if 0 then set DT1;   * ① ;
           length  _NAME  $40. 
        _TYPE  $1.;   * ② ;

           do until ( _NAME="" );  * ③ ;
               call vnext( _NAME, _TYPE, _LENGTH ); * ⓸ ;
               if  _NAME ^=""  then  output;
           end;

           stop;  * ⑤ ;
           keep _NAME _TYPE _LENGTH ;

        run;
        結果データ「DT2」



        解説

        ①
        変数定義を取得するデータセットを指定。「IF 0 THEN」の部分は大分特殊な事をしてるので詳細は割愛。ざっくり言うと、変数定義を取得したいだけなので、データセットの読み込みを一部省略したような動きをしています。

        ②
        変数定義を放り込む変数に、LENGTHを指定。

        ③
        CALL VNEXTは実行する度に、順に変数定義を読みに行きます。
        なので読み込む定義がなくなるまでDO UNTILでループします。

        ⓸
        変数名を「_NAME」、型を「_TYPE」、変数長を「_LENGTH」という変数に放り込むよう指定。
        もし変数名だけ取得したい場合、「call vnext( _NAME )」としてもOK。

        ⑤ 変数定義を取得した後は、STOPステートメントでデータステップを強制的に停止。


        POINT
        • 「_ERROR_」「_N_」などの自動変数の定義も読み込まれます。
        • プログラム中で作成した変数(今回の例では「_NAME」「_TYPE」「_LENGTH」)の定義も読み込まれます。またこれらと同じ名前の変数がSETで読み込んだデータセットにもある場合は正しく動かなくなるので、その時はプログラム中で作成する変数名を変える必要あり。
        • 型は文字変数であれば"C"、数値変数であれば"N"が入ります。
        • 得られる変数名の順番は、バージョンや環境によって異なる可能性があるので当てにしない方が良い。




        応用例

        全変数一括で何か処理したい場合、「CALL VNEXT」と「CALL EXECUTE」を組み合わせると便利です。

        CALL EXECUTEはデータステップ100万回の以下記事にて分かり易く解説されてるので参照下さい。
        http://sas-tumesas.blogspot.jp/2013/11/call-execute.html


        * 全変数を一括でRENAME(先頭に"X"を付与)するサンプル ;
        data _NULL_;

           if 0 then set DT1 ( keep= drop= ) ;   * ① ;
           length _NAME $40.;

           call execute( "DATA DT3; SET DT1; RENAME " );

           do until ( _NAME="" );
              call vnext( _NAME );

              /* ^in の後のカッコの中は大文字で指定 */
              if upcase( _NAME ) ^in ( "_NAME", "_ERROR_", "_N_", "" ) then
                 call execute( strip(_NAME) || " = X" || strip(_NAME) );  * ② ;
           end;

           call execute( "; RUN;" );
           stop;

        run;
        「CALL EXECUTE」で生成されるプログラム
         DATA DT3; SET DT1; RENAME
         A1 = XA1
         A2 = XA2
         A3 = XA3
         ; RUN;
        結果データセット「DT3」


        📝
        プログラム中の「_NAME」と同名の変数が①の部分でSETしたデータセットにもある場合は正しく動かなくなるので、その際はプログラム中の「_NAME」を別の名前に変える必要があります。



        解説

        最初のサンプルプログラムをもとに発展させたやり方です。

        ①
        RENAMEする変数を選択したい場合は、2行目の「if 0 then set DT1 (keep= drop=)」のところで、

        if 0 then set DT1 ( keep=A2 A3  drop= );

        としたり、RENAMEしたくない変数がある場合、

        if 0 then set DT1 ( keep=  drop=A1 );

        のようにすればOK。
        上2つのように書くと、以下のプログラムが生成されます。

        DATA DT3; SET DT1; RENAME
        A2                                      =XA2
        A3                                      =XA3
        ; RUN;


        ②
        自動変数等がRENAME対象に入らないよう除外した上で、CALL EXECUTEでRENAME処理の文を生成しています。
        SASのバージョンが上がって、もし自動変数が追加されてたら、ここをちょっと変える必要あり。



        工夫すればなんでも出来るので非常に使い勝手がいいです。
        変数を取得する方法は他にも沢山あり、「変数名を取得する方法 [まとめ]」でまとめてるので参考までに。