2018年11月20日火曜日

FORMATの落とし穴【デフォルト長】




PROC FORMAT の VALUEステートメントでありがちな落とし穴。
以下のプログラムと結果をご覧ください。結果は想定通りでしょうか?

proc format;
   value $FMT
       "a"    = "x"
       "abc" = "y"
   ;
run;

data DT1;
   format VAR1 $FMT.;
   VAR1 = "abc";
run;

  VAR1  
  x 

変数VAR1は "abc" なのでフォーマット$FMTを貼りつけると "y" と表示されるはずが、 "x" が表示されてしまいました。



原因

これは「デフォルト長 (Default Length)」というものが関係してます。
例えば、今回の例で

proc format;
   value $FMT
       "a"    = "x"
       "abc" = "y"
   ;
run;


等号の右側に定義した値 ("x" と "y") の文字の長さは1バイトです。
この長さが、そのフォーマットの「デフォルト長」というものになります。
(定義した値が複数ある場合は、右側の文字の長さが最も大きいものがそのフォーマットの「デフォルト長」になる)



そしてこのフォーマット$FMTは、以降のFORMATステートメントでデフォルト長となった1バイトしか視界に入らなくなります。
どういう事かというと、、

data DT1;
   format VAR1 $FMT.;
   VAR1 = "abc";
run;

変数VAR1 の値 "abc" から先頭の1バイトの値 "a" だけを見て対応するフォーマット値を表示しようとします。



ちょうど PROC FORMAT で "a" = "x" と定義してたので、この値が表示されちゃったというわけですね。

proc format;
   value $FMT
       "a"    = "x"
       "abc" = "y"
   ;
run;


ちなみに...

以下のように、PROC FORMATから1つ目の「"a" = "x"」をコメントアウトして定義から外すと、、、

proc format;
   value $FMT
/*       "a"    = "x" */
       "abc" = "y"
   ;
run;

data DT1;
   format VAR1 $FMT.;
   VAR1 = "abc";
run;

  VAR1  
  a 

まずは変数VAR1 の値 "abc" から先頭の1バイトの値 "a" だけを見て対応するフォーマット値を表示しようとしますが、
対応するフォーマットがないので、そのままの値 "a" が表示されちゃってるわけですね。
この辺の挙動を知らないと「なんじゃこりゃ??」となってしまいますね。



解決策

変数VAR1 の値 "abc" の文字の長さは3バイトなので、3バイト全部読み込んで表示する必要があります。
解決策としては以下の2つの方法があります。


解決策①
proc format;
   value $FMT (default=10)
       "a"    = "x"
       "abc" = "y"
   ;
run;

data DT1;
   format VAR1 $FMT.;
   VAR1 = "abc";
run;

  VAR1  
  y 

「DEFAULT=オプション」によってこのフォーマットのデフォルト長を 10 に広げています。


解決策②
proc format;
   value $FMT (max=10)
       "a"    = "x"
       "abc" = "y"
   ;
run;

data DT1;
   format VAR1 $FMT10.;
   VAR1 = "abc";
run;

  VAR1  
  y 

「format VAR1 $FMT10.」として、FORMATステートメントで、フォーマット名の後ろに幅を指定することも出来ます。
今回は幅を10に設定して問題なく動作していますが、「出力形式〇〇に指定した幅は無効です」みたいなERRORが出る事があります。そういった場合はFORMATプロシジャで「value $FMT (max=10)」というように、指定可能な幅の最大値を設定する必要があります。




See also

インフォーマットでも同様の落とし穴があります。
INFORMATの落とし穴【デフォルト長】


ポイント
・文字値から数値に変換するINVALUEステートメントでは「左側」に定義した文字の長さがデフォルト長になる
・VALUEステートメントでは「右側」に定義した文字の長さがデフォルト長になる


0 件のコメント:

コメントを投稿