TABULATE入門5

Last-modified: 2015-10-13 (火) 07:03:18
**************************************************************;
* TAB030402s5.sas                                            *;
* proc tabulate を使おう                                     *;
* section 5 : 事前加工をうまくする                           *;
* 2003.4.2, 翔                                            *;
* 2008.3.18, 改定1                                           *;
**************************************************************;

options nocenter;

libname mydata "c:\";


/*
前セッションと同じく
1年間にクレジットカードを利用したホテル利用者の特徴を学習させることを目的としたのデータ。1,450人分を使って,実行例を示します.
*/

data creditcard;
  infile "c:\train_space.txt" firstobs=2 dlm='09'x; /*外部ファイルから入力*/

          input id      hotel   xgender $       xage    xincome  xmarital $     xusage  xretail xrest   xenter  xtrans  xother;  /*読み込むデータを指定*/
run;

proc print data=creditcard(obs=5);run;
/*
 OBS   id   hotel   xgender   xage   xincome   xmarital   xusage   xretail   xrest   xenter   xtrans   xother

   1    1     0     female     60       .       single    300000    100000       1        0        0    30000
   2    2     1     male       40       .       single     30000     10000       0        0        0    30000
   3    3     0     male       40       .       single    100000    100000       1        0    10000   100000
   4    4     0     male       30       .       single    300000    300000   10000   100000    30000    10000
   5    5     0     male       40       .       single     10000     10000       1        0        0        1

*/


/*****************************************************
今回が最後のセクションです.
ここでは,tabulateにかける前に,ちょっとデータに工夫しておくと,
便利なことをいくつか紹介します.
1.サンプリングデータの集計
2.sum/meanを使った反応数/反応率
3.承認率と利用率と延滞率と利用単価と
4.金額変数向き汎用フォーマット
5.度数分布と平均を見たかったら
6.ディサイルセグメント集計
*/

/******************************************************
1.サンプリングデータの集計
他のプロシージャと同じく,tabulateもfreq文を使って,
各オブザベーションの度数を与えることができます.
仮に,creditcardのデータが,母集団から,男は,1/10,
女は,1/5でサンプリングされたデータだとすると,
母集団の度数にweight backした値でクロス表を
出力できます.
その際,事前にfreq変数を作っておきます.
デフォルト先と正常先のサンプリングレートが違う場合に,
よく使います.
*/

data credit;set creditcard;
  if xgender="male" then freq=10; /*男なら度数は10倍*/
  else            freq=5;  /*女なら度数は5倍*/
run;


proc tabulate data=credit  formchar="           " noseps missing format=comma9.;
  class xincome xgender;
  table all xincome
       ,(all xgender)*(n pctn*f=7.1)
       /rts=10;
  freq freq;   /* freq文で,変数freqに度数が与えられていることを指示*/
run;

/*
                                          xgender

                 All             female             male

              N      PctN       N      PctN       N      PctN

 All         12,990   100.0     1,510    11.6    11,480    88.4
 xincome
 .              330     2.5        10     0.1       320     2.5
 1            6,090    46.9       640     4.9     5,450    42.0
 100            845     6.5       295     2.3       550     4.2
 300          1,330    10.2       250     1.9     1,080     8.3
 500          1,415    10.9       125     1.0     1,290     9.9
 700          1,285     9.9        95     0.7     1,190     9.2
 1000         1,695    13.0        95     0.7     1,600    12.3

*/


/****************************************************************
2.sum/meanを使った反応数/反応率
PCTNを使わずに,反応率を表示させます.
ポイントは,反応=1・非反応=0を分類変数として扱わず,
分析変数とみなして,1の合計を反応数,平均を反応率とすることです.
メリットは,興味のない非反応数と非反応率を出力しないですむことで,
表示をすっきりさせることは,探索的作業には重要です.
*/


data credit;set creditcard;
  resp=(xrest>0);   /*前回の値が0より大きければ,真(1),そうでなければ偽(0)*/
run;

/*
PCTNを使った場合
*/

proc tabulate data=credit  formchar="           " noseps missing format=comma9.;
  class xincome resp;
  table all xincome
       ,(all resp)*(n rowpctn*f=7.1)
       /rts=10;
run;

/*

                                           resp

                 All                0                 1

              N     RowPctN     N     RowPctN     N     RowPctN

 All          1,450   100.0     1,108    76.4       342    23.6
 xincome
 .               34   100.0        25    73.5         9    26.5
 1              673   100.0       518    77.0       155    23.0
 100            114   100.0        97    85.1        17    14.9
 300            158   100.0       124    78.5        34    21.5
 500            154   100.0       108    70.1        46    29.9
 700            138   100.0       107    77.5        31    22.5
 1000           179   100.0       129    72.1        50    27.9
*/

/*
MEANを使った場合
*/

proc tabulate data=credit  formchar="           " noseps missing format=comma9.;
  class xincome;
  var resp;
  table all xincome
       ,resp*(n sum mean*f=percent9.1)
       /rts=10;
run;

/*
                      resp

              N        Sum      Mean

 All          1,450       342    23.6%
 xincome
 .               34         9    26.5%
 1              673       155    23.0%
 100            114        17    14.9%
 300            158        34    21.5%
 500            154        46    29.9%
 700            138        31    22.5%
 1000           179        50    27.9%
*/




/*
次の例は,ある専業の極度アップDMの反応データです.
LE件数XLE金額のクロス表が3つ並んでいて,上から順に,
DM発送数,反応数,反応率です.
LE件数も,LE金額も,それぞれ反応率に対して同程度の効きが見られますが,
LE件数XLE金額のクロス表上で見ると,LE金額のほうが本来影響力をもっており,
LE件数は見せかけであることがわかります.
(一般にリスクはLE金額よりLE件数が重要視されることと,逆ですね)


proc tabulate data=dmyes.mst4 formchar="           "
  missing noseps format=comma7.;
  class len1 lea1;
  var res3;
  table res3*(n sum mean*f=percent7.1)*(all len1)
        ,all lea1
     /rts=30;
  format lea1 amt.;
run;


                                                lea1-LE金額直近

                                All      0     -500    -1000   -1500   1500+

 res3-20  N        All         93,443  36,864  22,941  18,184   8,820   6,634
 日以内反          len1-LE
 応有無            件数直近
                   0           36,900  36,852      42       6       .       .
                   1           29,901      12  21,258   7,328     573     730
                   2           19,384       .   1,545  10,110   5,724   2,005
                   3            5,411       .      86     659   2,217   2,449
                   4            1,614       .       9      78     287   1,240
                   5              222       .       1       3      17     201
                   6               11       .       .       .       2       9
          Sum      All          4,775   1,398     979     965     667     766
                   len1-LE
                   件数直近
                   0            1,402   1,398       3       1       .       .
                   1            1,441       0     909     434      38      60
                   2            1,177       .      63     491     405     218
                   3              527       .       3      33     192     299
                   4              198       .       0       6      27     165
                   5               28       .       1       0       5      22
                   6                2       .       .       .       0       2
          Mean     All          5.1%    3.8%    4.3%    5.3%    7.6%   11.5%
                   len1-LE
                   件数直近
                   0            3.8%    3.8%    7.1%   16.7%        .       .
                   1            4.8%    0.0%    4.3%    5.9%    6.6%    8.2%
                   2            6.1%        .   4.1%    4.9%    7.1%   10.9%
                   3            9.7%        .   3.5%    5.0%    8.7%   12.2%
                   4           12.3%        .   0.0%    7.7%    9.4%   13.3%
                   5           12.6%        .   100%    0.0%   29.4%   10.9%
                   6           18.2%        .       .       .   0.0%   22.2%

*/



/****************************************************
3.承認率と利用率と延滞率と利用単価と
ローン申込データがあり,諾否,利用額,延滞有無が記録されているデータから,
承認率と利用率と延滞率と利用単価を計算することとします.
利用率は,申込先全体での利用者の比率ではなく,承認者のなかでの比率です.
同じように,延滞率は利用者中の延滞者率,利用単価も利用者中の利用額平均
とすべきでしょう.
このように,統計をとる範囲が全体と一致しない場合,異なる母数毎に
集計し直さなくても,対象外のデータを欠損にすることで,一度に対応できます.

注)消化率は,(単純平均でなければ)PCTSUMを使うしかなかったかも?
(忘れてしまった..)
*/


data cardloan;
  input CID sex $ decision $ balance delinquent;
cards;
001 M A 350000 1
002 M A 120000 0
003 M A 410000 0
004 M D      0 0
005 M D      0 0
006 F A 230000 1
007 F A  50000 0
008 F A      0 0
009 F D      0 0
;

data cardloan;set cardloan;
  approve=(decision="A"); /* 承認 */
  usage  =(balance>0);    /* 利用 */
  if approve ne 1 then usage=.; /*否認先なら利用(有無)は欠損*/
  if usage ne 1 then balance=.; /*未利用者なら残高は欠損*/
  if usage ne 1 then delinquent=.; /*未利用者なら延滞は欠損*/
run;

proc print;run;
/*
OBS    CID    sex    decision    balance    delinquent    approve    usage

 1      1      M        A         350000         1           1         1
 2      2      M        A         120000         0           1         1
 3      3      M        A         410000         0           1         1
 4      4      M        D              .         .           0         .
 5      5      M        D              .         .           0         .
 6      6      F        A         230000         1           1         1
 7      7      F        A          50000         0           1         1
 8      8      F        A              .         .           1         0
 9      9      F        D              .         .           0         .

*/


proc tabulate data=cardloan  formchar="           " noseps missing format=comma5.;
  class sex;
  var approve usage balance delinquent;
  table all sex
       ,n
        approve*(sum mean*f=percent9.1)
        usage*(sum mean*f=percent9.1)
        balance*(mean*f=comma9.)
      delinquent*(sum mean*f=percent9.1)
       /rts=10;
run;

/*
                    approve          usage       balance    delinquent

            N    Sum    Mean     Sum    Mean      Mean     Sum    Mean

 All          9     6    66.7%      5    83.3%    232,000     2    40.0%
 sex
 F            4     3    75.0%      2    66.7%    140,000     1    50.0%
 M            5     3    60.0%      3   100.0%    293,333     1    33.3%
*/

/****************************************************
4.金額変数向き汎用フォーマット
tabulateに限ったことではないですが,利用額,残高,収入など
指数分布のように,片裾の重いデータに対して,手っ取りばやく
区切って分布具合をみるのに便利なフォーマットの切り方を紹介します.
もし,平均と4分位などに見当がついているなら,それを目安に
フォーマットを区切ればいいですが,変数がたくさんあると,
いちいちそれに合わせて切るのは面倒です.
かといって,10万,20万,30万と等間隔で切るのは,
高額の部分でいきづまるし,対数をとると,いまいち見た目が
よくない..と私は感じます.
そういう時,なにも考えずに,
1万,3万,10万,30万,100万,300万..
と,1と3を交代させながら,指数的にフォーマットを
切ると,あら不思議,たいがいの指数的分布変数は,
うまい具合に散らばってくれます.
なぜ3を使っているのか,それは10の0.5乗が約3だからです.
*/


proc format;
  value amount
    low-0= "zero"
        0<-  10000="-10K"
    10000<-  30000="-30K"
    30000<- 100000="-100K"
   100000<- 300000="-300K"
   300000<-1000000="-1M"
  1000000<-high="1M+"
  ;
run;

proc tabulate data=creditcard  formchar="           " noseps missing format=comma5.;
  class xusage xenter;
  table all xusage xenter
       ,n
       /rts=10;
  format xusage xenter amount.;
run;

/*
            N

 All      1,450
 xusage
 zero         3
 -10K       314
 -30K       323
 -100K      413
 -300K      397
 xenter
 zero     1,197
 -10K        97
 -30K        75
 -100K       58
 -300K       2

*/

/****************************************************
5.度数分布と平均を見たかったら
もし,変数値の度数分布と平均値を同時に表示したければ,
前もって,その変数を別の変数にコピーしておく必要があります.
1つの変数を分類変数と分析変数の両方に宣言できないからです.
*/


data credit;set creditcard;
  xenter2=xenter;
run;

proc tabulate data=credit  formchar="           " noseps missing format=comma5.;
  class xenter;
  var   xenter2;
  table all xenter*n="" xenter2*mean
       ,all
       /rts=20;
  format xusage xenter amount.;
run;

/*
                     All

 All      N         1,450
 xenter
 zero               1,197
 -10K                  97
 -30K                  75
 -100K                 58
 -300K                 23
 xenter2  Mean      10834


*/


/****************************************************
6.ディサイルセグメント集計
ディサイルとは,10%刻みことです.
以下に,13万人のクレジットカード顧客に対し,その売上額順に
顧客を10%ずつのグループに分け,パレートの法則(20-80の法則)が
成り立っているか見てみます.
*/

/*

proc print data=usage(obs=10);run;


   OBS      usage

     1    1254920
     2      76731
     3          0
     4     672337
     5          0
     6          0
     7          0
     8          0
     9       2724
    10          0


data decile;set usage;
  keep usage ranuni;
  if usage=. then usage=0;
  ranuni=ranuni(1);
run;

proc sort data=decile;by usage ranuni;run;

data decile;set decile;n=_n_;call symput("n",n);run;

data decile;set decile;
  decile=int((&n-n)/&n*10)+1;
run;


proc tabulate data=decile formchar="           " missing noseps format=comma8.;
  class decile;
  var usage;
  table all decile
       ,usage*(n mean min max sum*f=comma16. pctsum)
  /rts=10;
run;


                                     usage

             N       Mean     Min      Max          Sum         PctSum

 All       130,493  157,254 -139,722 20378964   20,520,588,499      100
 decenti-
 le
 1          13,050  991,592  477,645 20378964   12,940,281,919       63
 2          13,049  333,816  226,997  477,642    4,355,958,789       21
 3          13,049  159,969  104,831  226,993    2,087,438,369       10
 4          13,050   68,100   37,990  104,795      888,704,600        4
 5          13,049   18,592    2,730   37,990      242,605,741        1
 6          13,049      479        0    2,730        6,253,346        0
 7          13,050        0        0        0                0        0
 8          13,049        0        0        0                0        0
 9          13,049        0        0        0                0        0
 10         13,049      -50 -139,722        0         -654,265       -0

*/

/*
事前に,売上額usageで,大きい順にdecileを1から10まで振り分けます.
tabulateでは,decile毎に,顧客数,平均売上額,最小値,最大値
売上額合計,構成比を表示します.(累積%ができるといいのに..)
この例だと上位20%(1+2decile)が,全体の売上の84%(=63+21)を
生み出していることがわかります.
*/


**********************************************************************;
************** E N D *************************************************;
**********************************************************************;

質問・意見

サマリ 質問の要旨を簡潔にお願いします。
お名前
状態
メッセージ

質問・意見一覧

一覧に表示する項目はありません.