ラベル ステートメント: ARRAY の投稿を表示しています。 すべての投稿を表示
ラベル ステートメント: ARRAY の投稿を表示しています。 すべての投稿を表示

2019年7月24日水曜日

ARRAYステートメントでのLENGTH指定





配列に定義した新たな変数に変数長(LENGTH)を割り当てたい場合は、以下のような感じで書きますが、、

data test;
    length a b c $10.;
    array ar(3) a b c;
run;




ARRAYステートメントで直接定義することも可能(※)

data test;
    array ar(3) $10. a b c;
run;




※ 注意点

SASの挙動として当たり前な話ではありますが、
データステップ内で変数に対して変数長が設定された後、途中で変数長を変えることは出来ません。


NG例①

以下のように、ARRAYステートメントで変数長を設定後、LENGTHステートメントで変数長を変えることは無理。

data test;
    array ar(3) $10. a b c;
    length a b c $20.;
run;

以下、PROC CONTENTSでの確認結果。










NG例②

逆もしかり、
LENGTHステートメントで変数長を設定後に、ARRAYステートメントで変数長を変えることは無理。
注目したいのが、例①ではログにWARNINGが出たのに対し、以下のようにARRAYステートメントで変数長を後出しした場合、WARNINGが出ません。

data test;
    length a b c $5.;
    array ar(3) $10. a b c;
run;









2017年9月7日木曜日

関数内での配列の使い方



「定義した配列を関数の中で使うにはどのように書けばよいですか?」

という質問を頂くことがあるので、簡単に紹介します。



* Sample data ;
data DT1;
    X=1;
    Y=2;
    Z=3;
run;






* 関数内で配列を使う例 ;
data DT2;
    set DT1;
    array AR(*) X Y Z;

    * 例① ;
    TOTAL1 = sum( AR(1), AR(2) );

    * 例② ;
    TOTAL2 = sum( of AR(*) );
run;



「例①」では配列からAR(1)とAR(2)を指定しています。
「例②」では配列ARの全要素を指定しています。

ひとつだけ注意点。
配列には色々な種類がありますが、その配列の種類によっては、例②の書き方が通用しない関数があります(実行時にERRORになる)



2015年8月24日月曜日

一時配列(ARRAY _TEMPORARY_)はRETAIN機能をもってる


「ARRAYステートメントにRETAIN機能を付与する小技」 の番外編。


ARRAYステートメントの変数名を指定する部分に「_TEMPORARY_」と指定すると、「一時データ要素」の配列を作ることが出来ます。

「一時データ要素」はデータステップ内でのみ存在し、終了したら消えます。

例
* テストデータ ;
data DT1;
input A;
cards;
1
.
.
2
.
;

 A  
 1 
 . 
 . 
 2 
 . 

* 一時配列の動きを確認してみる ;
data DT2;
   set DT1;
   array AR(1) _temporary_ ;
   if A^=. then AR(1) = A;
   A2=AR(1);
run;

 A  
 A2 
 1 
 1 
 . 
 1 
 . 
 1 
 2 
 2 
 . 
 2

一時データ要素はデータステップ終了とともに無くなってしまうので、適当にA2という変数を作って、そこに一時データ要素の値を入れて動きを確認しています。

A2を見ると、RETAINのように値が保持され続けてる事が確認できます。
この一時配列の性質を知っておくと結構役に立ちます。

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ループの組み合わせはよく見かける鉄板コンビです。



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へ順番に放り込んでいく。




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


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月20日木曜日

        欠損値の置き換え。



        SASのFAQや色々なサイトですでに紹介されていますが、
        面白いなぁと思ったので、こちらでも紹介したいと思います。


        指定した数値変数について、欠損値をゼロや任意の値に置き換えてくれる小技です。

        *** サンプルデータ ;
        data DT1;
        input A B C;
        cards;
        1 . 2
        3 4 .
        . . 5
        ;

        データセットDT1
        A   B   C
        1    .    2
        3   4    .
        .     .    5




        上記に対して、指定した数値変数が欠損値だったら「0」に置き換えたい場合、
        以下のようにARRAYを使って書くのがスタンダードだと思います。

        data DT2;
          set DT1;
          array AR(*) A B C;
          do i = 1 to dim(AR);
             if AR(i) =. then AR(i) = 0;
          end;
        run;

        データセットDT2
        A   B   C
        1    0    2
        3    4    0
        0    0    5

        このプログラムで何をやってるかは以下記事を見ていただくと理解が深まると思います。
        配列(ARRAYステートメント)入門





        そしてここから本題。面白いと思ったのが、以下のやり方。

        proc stdize data=DT1 reponly missing=0 out=DT3;
           var A B C;
        run;

        STDIZEプロシジャは、データの標準化をするSTANDARDプロシジャの進化版みたいなもの。
        「reponly」と「missing=」オプションを指定することで、欠損値の置き換えのみを行ってくれる。


        こういうプロシジャの面白い使い方をどんどん発掘していきたいです。