allvarcomb

総当たり法によるモデル選択

回帰モデル等の解析におけるモデル選択を考えます。 例えば、説明変数が $x_1$ と $x_2$ だった場合、$y = x_1$, $y = x_2$, $y = x_1 + x_2$ の 3 通りのモデルのうち、どのモデルが一番良いモデルなのかを検討する状況を想定します。

上の例のように 2 変数程度であれば、コピー & ペーストして変数を修正したものを作れば充分です。 しかし、$p$ 変数になった場合、$2^p - 1$ 通りの組み合わせを生成する必要があり、5 変数もあれば コピー & ペーストは効率的ではないでしょう。

$p$ と $2^p - 1$ の関係

$p$ $2^p - 1$
5 31
6 63
7 127
8 255
9 511
10 1023
$p$ $2^p - 1$
15 32767
20 1048575
25 33554431
30 1073741823
40 1099511627775
50 1125899906842620

%allvarcomb() マクロ

今回は $p$ 変数の全ての組み合わせを列挙する %allvarcomb() マクロを作成しました。
引数は、var: 変数リスト (スペース区切り)、out: 出力データセット名、info: 組み合わせ数と変数の数を記録するデータセット名、です。


  %macro bincombinations(p, out);
  data &out.;
    array v[*] v1-v%eval(2**&p.);
    retain f 0;
    do j = 1 to &p.;
      pn = 2**&p./2**j;
      do i = 1 to 2**&p.;
        v[i] = f;
        if mod(i, pn) = 0 then f = mod(f+1, 2);
      end;
      output;
    end;
    keep v1-v%eval(2**&p.);
  proc transpose data = &out.
    out = &out.(drop=_NAME_) prefix = v;
  run;
  %mend bincombinations;

  %macro allvarcomb(var, out, info);
  data &out.;
    string = "&var.";
    do until(position <= 0);
      count + 1;
      call scan(string, count, position, length, ' ');
      word = substrn(string, position, length);
      if word ^= "" then output;
    end;
    keep word;
  data _null_; set &out.;
    call symput("vnum", compress(_n_));
  proc transpose data = &out.
    out = &out.(drop=_NAME_) prefix = vn;
    var word;
  %bincombinations(&vnum., vs);
  data &out.; set vs;
    if _n_ = 1 then set &out.;
  data &out.; set &out.;
    array avn[*] vn1-vn&vnum.;
    array av[*] v1-v&vnum.;
    do i = 1 to &vnum.;
      if av[i] = 0 then avn[i] = "";
    end;
    if _n_ ^= 1 then output;
    call symput("cnum", compress(_n_)-1);
    keep vn1-vn&vnum.;
  data &info.;
    cnum = &cnum.; vnum = &vnum.;
  proc datasets lib = work; delete vs; run; quit;
  %mend allvarcomb;
  

モデル選択基準を計算する部分のマクロです。
真ん中の部分を解析目的に応じて変更して利用します。
以下の例では、GENMOD Procedure で正規線型モデルを当てはめて AIC を求めています。


  %macro modelselect(y, x, data, out, class=);
  proc datasets lib = work; delete &out.;
  %if       "&class." = "1" %then %let class = class &x.;
  %else %if "&class." = ""  %then %let class = ;
  %else                           %let class = class &class.;
  %allvarcomb(&x., allvarcomb, info);
  data _null_; set info;
    call symput("cnum", cnum);
    call symput("vnum", vnum);
  run;
  ods listing close;
  ods results off;
  %do i = 1 %to &cnum.;
  data _null_;
    set allvarcomb(firstobs=&i. obs=&i.);
    %do j = 1 %to &vnum.;
      call symput("vn&j.", vn&j.);
    %end;
  proc datasets lib = work; delete temp;
  /************************************
  modify for any purpose
  ************************************/
  proc genmod data = &data.;
  &class.;
  model &y. =
    %do j = 1 %to &vnum.;
      &&vn&j. 
    %end; /
    link = id dist = normal;
    ods output ModelFit = temp;
  run;
  data temp; set temp(obs=7 firstobs=7); keep value;
  /************************************/
  data &out.;
    set %if "&i." ^= "1" %then &out.; temp;
  %end;
  ods results on;
  ods listing;
  data &out.; merge &out. allvarcomb;
  proc datasets lib = work; delete allvarcomb info temp;
  run; quit;
  %mend modelselect;
  

$7$ 変数のテストデータを生成し、$AIC$ によるモデル選択を行います。
$7$ 変数なので、$2^7 - 1 = 127$ 通りのモデルを全て当てはめて、$AIC$ を求めます。


  /* テストデータの生成 */
  data testdata;
    do var = 0, 1; do uvar = 0, 1; do xbar = 0, 1;
    do avar = 0, 1; do x1 = 0, 1; do x2 = 0, 1; do xe = 0, 1;
      y = rand("Normal", 0*var + 1*uvar + 5*xbar + 5*avar + 1*x1 + 0*x2 + 1*xe, 1);
      output;
    end; end; end; end; end; end; end;
  run;

  filename fdummy dummy;
  proc printto log = fdummy;
  run;
  %modelselect(y, var uvar xbar avar x1 x2 xe, testdata, aic, class = 1);
  proc printto log = log;
  run;

  proc rank data = aic out = aic;
  var value; ranks aicrank;
  proc sort; by aicrank;
  proc print;
    where aicrank <= 10;
  run;
  

      Value    vn1    vn2     vn3     vn4     vn5    vn6    vn7    aicrank

  341.2267           uvar    xbar    avar    x1            xe         1
  343.0875           uvar    xbar    avar    x1     x2     xe         2
  343.2253    var    uvar    xbar    avar    x1            xe         3
  345.0861    var    uvar    xbar    avar    x1     x2     xe         4
  374.1257           uvar    xbar    avar    x1                       5
  376.0197           uvar    xbar    avar    x1     x2                6
  376.1247    var    uvar    xbar    avar    x1                       7
  378.0187    var    uvar    xbar    avar    x1     x2                8
  380.3804           uvar    xbar    avar                  xe         9
  382.2795           uvar    xbar    avar           x2     xe        10
  

モデル y = uvar + xbar + avar + x1 + xe が選択されました。
これはテストデータで生成したモデルと一致しているので、うまく選択できているようです。

また、%allvarcomb() でどのようなデータが生成されているのかを見たい場合は、以下を実行します。


  %allvarcomb(var uvar xbar avar x1 x2 xe, allvarcomb, info, class = 1);
  proc print data = allvarcomb; run;
  

ダウンロード

履歴

  • 2014/09/06 マクロの改良 (結果変数 y を指定できる様に変更; 連続型共変量への対応; Resultsのリストを表示しないように変更)
    • 結果変数 y を指定できる様に変更
    • 離散型、連続型共変量が混ざっている場合に対応 (引数 class を追加; class = 1: 全部離散型, class = 変数名リスト: 指定変数だけ離散型, class = (ブランク): 全部連続型)
    • Resultsのリストを表示しないように変更
  • 2010/06/25 公開