2018年1月31日水曜日

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




以下のプログラムと結果をご覧ください。結果は想定通りでしょうか?

proc format;
    invalue MYFMT
    "abc"   = 1
    ;
run;

data DT1;
    X = "abcdefg";
    Y = input( X , MYFMT. );
run;

  X    Y  
  abcdefg   1 

変数Y が 1 という結果はおかしいですよね。これって

・INFORMATの値   …  "abc"
・変数X の値           …  "abcdefg"

といった感じで、値が一致してないのに、一致してるかのように変換が行われています。




原因

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

proc format;
    invalue MYFMT
    "abc"   = 1
    ;
run;

左側に定義した値 "abc" の文字の長さは3バイトです。
文字値から数値に変換するINFORMATの場合、この長さがそのINFORMATの「デフォルト長」というものになります。
(定義した値が複数ある場合は、文字の長さが最も大きいものがそのINFORMATの「デフォルト長」になる)



そしてこのINFORMATは、以降の処理でデフォルト長となった3バイトしか視界に入らなくなります。
どういう事かというと、、

data DT1;
    X = "abcdefg";
    Y = input( X , MYFMT. );
run;

変数X の値 "abcdefg" から先頭の3バイトの値 "abc" だけを見てINFORMATによる変換が行われるという事です。



ちょうど PROC FORMAT で"abc" を定義してたので、変換しちゃったってわけですね。

proc format;
    invalue MYFMT
    "abc"   = 1
    ;
run;



解決策

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


解決策①
proc format;
    invalue MYFMT (default=20)
      "abc"   = 1
    ;
run;

data DT1;
    X = "abcdefg";
    Y = input( X, MYFMT. );
run;

   Y  
  abcdefg   . 

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


解決策②
proc format;
    invalue MYFMT
      "abc"   = 1
    ;
run;

data DT1;
    X = "abcdefg";
    Y = input( X, MYFMT20. );
run;

   Y  
  abcdefg   . 

INPUT関数による変換時に、INFORMAT名の後ろに読み込む幅を指定することも出来ます。




See also

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


ポイント
・文字値から数値に変換するインフォーマットの場合、「左側」に定義した文字の長さがデフォルト長になる。
・フォーマットの場合、「右側」に定義した文字の長さがデフォルト長になる。

0 件のコメント:

コメントを投稿