The Nameless City

何故か製薬やSAS関連のブログ、の予定。

コードリストになっている文字列から、コード値の数値に変換する方法

要約

コード変換については、SASの場合にはフォーマット/インフォーマットを使うのが楽です。

数値コード→文字列変換
数値フォーマットを使います。
文字列コード→文字列変換
文字フォーマットを使います。
文字列→数値コード変換
数値のインフォーマットを使います。

インフォーマットとは。

聞きなれない言葉かもしれませんが、、、外部の文字列を取り込んでSASの変数に格納する際に適用される、フォーマットの逆みたいなやつです。
普段はあんまり使わないんですが、CSVからデータ取り込みするとかの場合に適用されます。
また、ほとんどの場合にはフォーマットと対になるインフォーマットがあるんですが、例外も結構あります。
日付型のYYMMDD10.フォーマットは、亜種にYYMMDDS10.フォーマットとかあるんですが、インフォーマットはYYMMDD10,インフォーマットしかないです。スラッシュ区切りでもYYMMDD10.インフォーマット使えばいいのですが。

作り方

こんな感じ。

/* S-JISでやってます */
proc format ;
  invalue _SEXI '男子'=1 '女子'=2 ;
quit ;
data CLASS ;
  set SASHELP.CLASS ;
  attrib SEXN length=8 label='SEX (N)' ;
  keep NAME SEX SEXN ;
  SEXN = input(SEX,?? _SEXI.) ;
run ;
proc print data=CLASS ;
quit ;
OBS    Name            Sex     SEXN

  1    アルフレッド    男子      1
  2    アリス          女子      2
  3    バーバラ        女子      2
  4    キャロル        女子      2
  5    ヘンリー        男子      1
  6    ジェームズ      男子      1
  7    ジェーン        女子      2
  8    ジャネット      女子      2
  9    ジェフリー      男子      1
 10    ジョン          男子      1
 11    ジョイス        女子      2
 12    ジュディー      女子      2
 13    ルイーズ        女子      2
 14    メアリー        女子      2
 15    フィリップ      男子      1
 16    ロバート        男子      1
 17    ロナルド        男子      1
 18    トーマス        男子      1
 19    ウィリアム      男子      1

注意点

このままだと、実はアサインされていない文字列の場合には、bestで適当に数値変換されてしまいます。
通常は文字列を数値に変換しても問題ないのですが、たまたま数値にうまく変換出来る場合であると、変な事になります。

proc format ;
  invalue _SEXI '男子'=1 '女子'=2 ;
quit ;
data CLASS ;
  set SASHELP.CLASS ;
  attrib SEXN length=8 label='SEX (N)' ;
  keep NAME SEX SEXN ;
  if SEX = '男子' then SEX='3.14' ;
  SEXN = input(SEX,?? _SEXI.) ;
run ;
proc print data=CLASS ;
quit ;
OBS    Name            Sex     SEXN

  1    アルフレッド    3.14    3.14
  2    アリス          女子    2.00
  3    バーバラ        女子    2.00
  4    キャロル        女子    2.00
  5    ヘンリー        3.14    3.14
  6    ジェームズ      3.14    3.14
  7    ジェーン        女子    2.00
  8    ジャネット      女子    2.00
  9    ジェフリー      3.14    3.14
 10    ジョン          3.14    3.14
 11    ジョイス        女子    2.00
 12    ジュディー      女子    2.00
 13    ルイーズ        女子    2.00
 14    メアリー        女子    2.00
 15    フィリップ      3.14    3.14
 16    ロバート        3.14    3.14
 17    ロナルド        3.14    3.14
 18    トーマス        3.14    3.14
 19    ウィリアム      3.14    3.14

これを回避するのには、ちょっと仕込みがあります。
otherの設定をしてやると良いです。

proc format ;
  invalue _SEXI '男子'=1 '女子'=2 other=. ;
quit ;
data CLASS ;
  set SASHELP.CLASS ;
  attrib SEXN length=8 label='SEX (N)' ;
  keep NAME SEX SEXN ;
  if SEX = '男子' then SEX='3.14' ;
  SEXN = input(SEX,?? _SEXI.) ;
run ;
proc print data=CLASS ;
quit ;
OBS    Name            Sex     SEXN

  1    アルフレッド    3.14      .
  2    アリス          女子      2
  3    バーバラ        女子      2
  4    キャロル        女子      2
  5    ヘンリー        3.14      .
  6    ジェームズ      3.14      .
  7    ジェーン        女子      2
  8    ジャネット      女子      2
  9    ジェフリー      3.14      .
 10    ジョン          3.14      .
 11    ジョイス        女子      2
 12    ジュディー      女子      2
 13    ルイーズ        女子      2
 14    メアリー        女子      2
 15    フィリップ      3.14      .
 16    ロバート        3.14      .
 17    ロナルド        3.14      .
 18    トーマス        3.14      .
 19    ウィリアム      3.14      .