在 PROC GLM 和 PROC MIXED 裡面,最令一般 SAS 使用者感到頭痛的不是模型的檢定而是事後分析(post hoc test)。這牽涉到 contrast 和 estimate 兩個語法。在我接觸過所有非統計科班出身但需要用到線性模型以及事後分析的人,沒有人不對這兩個語法感到聞風喪膽。多年人我曾經試圖用各種方法來教人如何使用這兩種語法,但效果都不怎麼顯著。簡單來講,Contrast 和 estimate 無論是在 PROC GLM 還是 PROC MIXED 裡面,功能完全一樣,語法內的選項也一樣,而兩者之間唯一的差別是,contrast 可以只能算出 p-value,而 estimate 可以把指定的點估計量及其 p-value 一次輸出。David J. Pasta 在 SAS Global Forum 2010 發表了一篇技術文件將這兩種語法做一個詳盡的介紹,堪稱一大貢獻。在本文中,我會介紹幾個比較實用的範例。
原文作者套用一個醫學資料並詳述其背景,但為了簡化初學者的時間,我將會以程式來主。第一個範例資料只有三個變數,分別是作為應變數的離散型變數 sex (兩層) 和 race (五層),而依變數則為一個連續型變數 y1。有了對資料的基本概念,首先來看一個最簡單的例子:
proc glm data=anal;
      class sex;
      model y1 = sex / solution;
      lsmeans sex / stderr tdiff e;
run;proc glm data=anal;
      class sex;
      model y1 = sex / solution;
      estimate 'male' intercept 1 sex 0 1;
      estimate 'female' intercept 1 sex 1 0;
      estimate 'female-male' sex 1 -1;
run;而當要比較女生和男生的差異時,則有兩點要注意。一是 intercept 可以不用寫了,因為兩兩相減的情況下,截距項自然就被減掉了。二是 sex 後面要改成「1 -1」,很明顯地這就是指用女生的估計值去減男生的估計值。如果要算男生的估計值減女生的估計值,把「1 -1」改成「-1 1」即可。偷懶的方法是不改,但把 output 輸出的估計值變號即可,至於 p-value 是不會改變的。
當考慮兩個應變數時,程式如下:
proc glm data=anal;
      class race sex;
      model y1 = race sex / solution e;
      lsmeans race sex / stderr tdiff e;
      lsmeans race sex / stderr tdiff e om;
run;若用 estimate 語法則程式如下:
proc glm data=anal;
      class race sex;
      model y1 = race sex / solution e;
      estimate 'male' intercept 1 sex 0 1;
      estimate 'female' intercept 1 sex 1 0;
      estimate 'female-male' sex 1 -1;
run;當考慮交互作用項時,estimate 語法就沒有辦法簡化了:
proc glm data=anal;
      class sex race;
      model y1 = race sex race*sex / solution;
      estimate 'male'   intercept 1 sex 0 1 race .2 .2 .2 .2 .2  race*sex 0 0 0 0 0  .2 .2 .2 .2 .2;
      estimate 'female' intercept 1 sex 1 0 race .2 .2 .2 .2 .2  race*sex .2 .2 .2 .2 .2  0 0 0 0 0;
      estimate 'female-male' sex 1 -1 race*sex .2 .2 .2 .2 .2  -.2 -.2 -.2 -.2 -.2;
run;proc iml;
      a = {0 1};
      b = {0.2 0.2 0.2 0.2 0.2};
      ab = a@b;
      print ab;
quit;可是當考慮權重時,程式如下:
proc glm data=anal;
      class sex race;
      model y1 = race sex race*sex / solution e;
      estimate 'male' intercept 1  sex 0 1  race  .104 .142 .128 .047 .579 race*sex 0 0 0 0 0  .104 .142 .128 .047 .579;
      estimate 'female' intercept 1  sex 1 0 race  .104 .142 .128 .047 .579 race*sex .104 .142 .128 .047 .579  0 0 0 0 0;
      estimate 'female-male' sex 1 -1 race*sex .104 .142 .128 .047 .579 -.104 -.142 -.128 -.047 -.579;
      estimate 'female-male (off)' sex 1 -1 race*sex .105 .142 .128 .047 .579 -.105 -.142 -.128 -.047 -.579;
run;有時候事後比較的目的是要看某離散型的變數是否有線性趨勢,則程式如下:
proc glm data=anal;
      class sex educat;
      model y1 = sex educat / solution;
      lsmeans sex educat / stderr e;
      lsmeans sex educat / stderr e om;
      estimate 'educat linear (no divisor)' educat -2 -1 0 1 2;
      estimate 'educat linear (divisor=10)' educat -2 -1 0 1 2 / divisor=10;
      estimate 'educat linear (divisor=40)' educat -4 -2 0 2 4 / divisor=40;
      estimate 'educat by year (not centered)' educat 8 12 14 16 20;
      estimate 'educat by year (centered)' educat -6 -2 0 2 6 / divisor=80;
run;estimate 'educat linear (divisor=10)' educat -0.2 -0.1 0 0.1 0.2;proc glm data=anal;
      class sex race;
      model y1 = race sex race*sex / solution e;
      estimate 'male (333)' intercept 1  sex 0 1  race  .333 .333 .333 race*sex 0 0 0  .333 .333 .333; 
      estimate 'female (333)' intercept 1  sex 1 0 race  .333 .333 .333 race*sex  .333 .333 .333  0 0 0; 
      estimate 'female-male (333)' sex 1 -1 race*sex  .333 .333 .333  -.333 -.333 -.333; 
      estimate 'male (334)' intercept 1  sex 0 1  race  .333 .333 .334 race*sex 0 0 0  .333 .333 .334; 
      estimate 'female (334)' intercept 1  sex 1 0 race  .333 .333 .334 race*sex  .333 .333 .334  0 0 0; 
      estimate 'female-male (334)' sex 1 -1 race*sex  .333 .333 .334  -.333 -.333 -.334; 
      estimate 'male (d)' intercept 3  sex 0 3  race  1 1 1  race*sex 0 0 0  1 1 1 / divisor=3; 
      estimate 'female (d)' intercept 3  sex 3 0 race  1 1 1  race*sex 1 1 1  0 0 0 / divisor=3; 
      estimate 'female-male (d)' sex 3 -3 race*sex 1 1 1   -1 -1 -1 / divisor=3; 
      estimate 'male (o)' intercept 849  sex 0 849  race  142 128 579 race*sex 0 0 0  142 128 579 / divisor=849; 
      estimate 'female (o)' intercept 849  sex 849 0 race  142 128 579 race*sex 142 128 579  0 0 0 / divisor=849; 
      estimate 'female-male (o)' sex 849 -849 race*sex 142 128 579 -142 -128 -579 / divisor=849; 
run;data _null_;
    x = lcm(a1,a2,....,aN);
    put x;
run;以上就是很基本但卻很實用的 estimate 寫法。contrast 的用法一模一樣,所以沒有多做介紹。原文後面還有一些更進階的語法,但我個人的經驗是極少數的情況會使用到,因此大家熟練以上的程式就夠了。不過有時候離散變數內的層數很多,在建立 estimate 語法的時候,後面會跟著一長串的數字向量,非常驚人(恐怖)。原文作者建議將一些會一直重複用到的數字向量用 %let 先定義好,等到要用的時候再呼叫即可,比方說:
%let S_1    = 1 0 0 0 0 0 0 0 0 0;
%let S_2    = 0 1 0 0 0 0 0 0 0 0;
%let S_3    = 0 0 1 0 0 0 0 0 0 0;
%let S_4    = 0 0 0 1 0 0 0 0 0 0;
%let S_5    = 0 0 0 0 1 0 0 0 0 0;
%let S_6    = 0 0 0 0 0 1 0 0 0 0;
%let S_7    = 0 0 0 0 0 0 1 0 0 0;
%let S_8    = 0 0 0 0 0 0 0 1 0 0;
%let S_9    = 0 0 0 0 0 0 0 0 1 0;
%let S_10   = 0 0 0 0 0 0 0 0 0 1;
%let S_none = 0 0 0 0 0 0 0 0 0 0;
%let P_U    = .1 .1 .1 .1 .1 .1 .1 .1 .1 .1;
%let P_O    = 0.0666048524 0.0776077886 0.0888270746 
              0.0955339206 0.1017462525 0.1059496214 
              0.1124710246 0.1164580436 0.1168907433 
              0.1179106784 ;
  *** Intercept at the end of the BEFORE and at the beginning of the AFTER ***;
  estimate 'LS:1_Before'   intercept             1
                           decile                &S_1.
                           decile*tafter         &S_none.;      
  estimate 'LS:1_After'    intercept             1
                           decile                &S_1.
                           decile*tafter         &S_1.;   
  estimate 'LS:2_Before'   intercept             1
                           decile                &S_2.
                           decile*tafter         &S_none.;      
  estimate 'LS:2_After'    intercept             1
                           decile                &S_2.
                           decile*tafter         &S_2.;      
 . . . 
  estimate 'LS:10_Before'  intercept             1
                           decile                &S_10. 
                           decile*tafter         &S_none.;      
  estimate 'LS:10_After'   intercept             1
                           decile                &S_10.
                           decile*tafter         &S_10.;      
  estimate 'LS:Before_U'   intercept             1
                           decile                &P_U.
                           decile*tafter         &S_none.; 
  estimate 'LS:After_U'    intercept             1
                           decile                &P_U.
                           decile*tafter         &P_U.;  
  estimate 'LS:Before_O'   intercept             1
                           decile                &P_O.
                           decile*tafter         &S_none.;   
  estimate 'LS:After_O'    intercept             1
                           decile                &P_O.
                           decile*tafter         &P_O.;proc sort data=temp01(keep=patid_age decile) out=temp02 nodupkey;
  by patid_age decile;
run;
data _null_;
  set temp02;
  by patid_age decile;
  if not(first.patid_age and last.patid_age) 
then error 'ERROR: decile varies within patid_age';
run;
ods listing close;
ods output onewayfreqs = temp03(keep=decile frequency cumfrequency);
proc freq data = temp02;
  tables decile;
run;
ods listing;
data _null_;
  set temp03;
  call symput('N_'||left(trim(put(decile,2.))),frequency);
run;
data _null_;
  set temp03;
  if (decile eq 10) then call symput('Ntot',cumfrequency);
run;
data _null_;
  call symput('P_1', &N_1. / &Ntot.));
  call symput('P_2', &N_2. / &Ntot.);
  call symput('P_3', &N_3. / &Ntot.);
  call symput('P_4', &N_4. / &Ntot.);
  call symput('P_5', &N_5. / &Ntot.);
  call symput('P_6', &N_6. / &Ntot.);
  call symput('P_7', &N_7. / &Ntot.);
  call symput('P_8', &N_8. / &Ntot.);
  call symput('P_9', &N_9. / &Ntot.);
  call symput('P_10', &N_10. / &Ntot.);
run;
*** Macro variable for model specifications ***;
%let P_O = &P_1. &P_2. &P_3. &P_4. &P_5. &P_6. &P_7. &P_8. &P_9. &P_10.;proc freq data=xxx;
      tables varcat;
      ods output onewayfreqs = temp(keep=percent);
run;proc transpose data=temp out=temp;
      var percent;
run;data _null_;
      set temp;
      call symput('P_1', COL1);
      call symput('P_2', COL2);
      call symput('P_3', COL3);
      call symput('P_4', COL4);
      call symput('P_5', COL5);
      call symput('P_6', COL6);
      call symput('P_7', COL7);
      call symput('P_8', COL8);
      call symput('P_9', COL9);
      call symput('P_10', COL10); 
run;%let P = &P_1 &P_2 &P_3;CONTACT INFORMATION
Your comments and questions are valued and encouraged. Contact the author at:
David J. Pasta
Vice President, Statistics & Data Operations
ICON Clinical Research
188 Embarcadero, Suite 200
San Francisco, CA 94105
+1.415.371.2111
david.pasta@iconplc.com
www.iconplc.com
 
 
沒有留言:
張貼留言
要問問題的人請在文章下方的intensedebate欄位留言,請勿使用blogger預設的意見表單。今後用blogger意見表單留言的人我就不回應了。