Summtable source code preview

From sasCommunity
Jump to: navigation, search
%MACRO summtable(data=,
                 class=,
                 con1=,
                 con2=,
                 con3=,
                 cat1=,
                 cat2=,
                 ord1=,
                 subset=,
                 colpct=1,
                 pvalues=1,
                 cutexact=50,
                 totalcol=1,
                 ncol=0,
                 misscol=0,
                 adhoc=0,
                 oddsratios=0,
                 unitscon1=,
                 unitscon2=,
                 unitscon3=,
                 sortby=,
                 list=,
                 out=_out_,
                 rtfout=_rtf_,
                 rtffile= ,
                 xmlfile= ,
                 pdffile= ,
                 odsmemo=0,
                 desclabel=0,
                 printptype=1,
                 sdopt=1,
                 tbltitle=  ,
                 bdytitle=0,
                 style=journal,
                 page=portrait,
                 printfn=1,
                 addfn=,
                 daytime=0,
                 f1= __Countf.,
                 f2= __Estf.,
                 f3= __Pctf.,
                 f4= __Estf.,
                 pfmt= Pvaluef.,
                 lablen = $40,
                 perfmt=5.1,
                 cwidth1=85,
                 cwidth2=,
                 cwidth3=,
                 printp=0,
                 prints=2);
 
*********************************************************************;
*********************************************************************;
*****************     Local macro variables    **********************;
*********************************************************************;
*********************************************************************;
%LOCAL VALID DATEON ERRORFLG LEVELS LOOP CLASSTYP
       NMCON1 NMCON2 NMCON3 NMCAT1 NMCAT2 NMORD1
       NMSUR1 NMSUR2 NMSUR3 NMSUR4 NMLIST NOMORE COUNT
       ICON1 ICON2 ICON3 ICAT1 ICAT2 IORD1 ILIST
       CON1V CON2V CON3V CAT1V CAT2V ORD1V
       LISTV TOTNM TOTVARN
       A1 A2 A3 A4 B1 B2 B3 B4 EVEN1 EVEN2 EVEN3 EVEN4 FN FLOOP
       ICAT RMSTRING CATVARN ICON NOWCON AOVL KWTL CONVARN
       IORD NOWORD ORDVARN ISUR SUR SUBTRACT PERCMULT
       VFCLASS
       LEV DATENOW TIMENOW CW1 CW2 FPLUS1
       Space1 Space2 MemName_ LibName_
       NObsAD LowestL _AAD _BAD CatListAD CatExactListAD
       Con1ListAD Con23ListAD NC1 _A _B NC1 _A _B ORCA1 ORCA2
       NOWCAT LOWL MAXL ORORD NOWORD ORCO1 NOWCON CURRENT CURRENTTITLE
       ODSMEMO;
 
*********************************************************************;
*********************************************************************;
*****************         System options       **********************;
*********************************************************************;
*********************************************************************;
%let valid=%sysfunc(getoption(validvarname));
%let dateon=%sysfunc(getoption(nodate));
 
options validvarname=v7 nodate noquotelenmax nonumber;
 
*********************************************************************;
*********************************************************************;
*****************    Include default formats   **********************;
*********************************************************************;
*********************************************************************;
proc format;
   picture Pvaluef (round)
           0.985   -   high    = "0.99"    (NoEdit)
           0.10    -<  0.985   = "9.99"
           0.001   -<  0.10    = "9.999"
           0       -<  0.001   = "<0.001"  (NoEdit)
           . = " ";
   picture __Estf (round)
           low   -  -1.1  = "00000000000009.9" (prefix='-')
          -1.1  <-<  0    = "9.99" (prefix='-')
             0   -<  1.1  = "9.99"
           1.1   -   high = "00000000000009.9";
   picture __Countf (round)
           low-high         = "0000000000000009"
           . = " ";
   picture __Pctf (round)
            low    - 0     = "9.9"
              0    <-< 1     = "9.99"
              1    -  high    = "00000000000009.9";
 
 
*********************************************************************;
*********************************************************************;
************ If ODSMEMO = 1 then make sure nothing prints ***********;
************ Also make sure no ods destination is called  ***********;
*********************************************************************;
%IF &ODSMEMO=1 %THEN %DO;
    ods listing close;
    %LET PRINTP=0;
    %LET RTFFILE= ;
    %LET XMLFILE= ;
    %LET PDFFILE= ;
    ods rtf exclude all;
%END;
 
*********************************************************************;
*********************************************************************;
*****************      Check errors in call    **********************;
*********************************************************************;
*********************************************************************;
%LET ERRORFLG=0;
 
***** Check if data set specified in call *****;
%IF (&DATA= ) %THEN %DO;
    %PUT ERROR: No input data set identified in DATA parameter.
    Macro will stop executing.;
    %LET ERRORFLG=1;
%END;
 
***** Check if data set exists *****;
%IF %SYSFUNC(exist(&DATA)) ne 1 %THEN %DO;
    %PUT ERROR: Data set &DATA does not exist. Macro will stop executing;
    %LET ERRORFLG=1;
%END;
 
***** Continue Checks if data exists *****;
%IF &ERRORFLG^=1 %THEN %DO;
 
    ***** Create working data set, subset if requiered *****;
    data _tempd_;
        set &DATA;
        *** Subset if requested ***;
        %IF (&SUBSET^= ) %THEN %DO;
            where &SUBSET;
        %END;
 
        *** We dont want the name of the class variable to cause problems
            in  ODS data sets. ;
        %IF (&CLASS= ) %THEN %DO;
            ___class="Total";
        %END;
        %ELSE %DO;
            if &CLASS=. then put
            "WARNING: Observations deleted due to missing &CLASS variable.";
            if &CLASS=. then delete; /* (RL) Avoid problems with missing group*/
            rename &CLASS = ___class;
        %END;
    run;
 
    ***** (R.L) Check all specified variables are in data set. If not, drop from analysis *****;
    %macro vexist(List, type);
 
        %LET space1=%SCAN(_tempd_,1);
        %LET space2=%SCAN(_tempd_,2);
 
        %IF (&SPACE2= ) %THEN %DO;
            %LET MEMNAME_=%UPCASE(&SPACE1);
            %LET LIBNAME_=WORK;
        %END;
        %ELSE %IF (&SPACE2^= ) %THEN %DO;
            %LET MEMNAME_=%UPCASE(&SPACE2);
            %LET LIBNAME_=%UPCASE(&SPACE1);
        %END;
 
        %LET NEW&TYPE= ;
        %LET ICOUNT=1;
        %LET CVAR=%SCAN(&LIST,&ICOUNT);
        %LET NEXVAR=&CVAR;
 
        %IF (&LIST^= ) %THEN %DO %WHILE(&NEXVAR NE );
 
            %LET CVAR=%UPCASE(&NEXVAR);
            %LET ICOUNT=%EVAL(&ICOUNT+1);
            %LET NEXVAR=%SCAN(&LIST,&ICOUNT);
 
            proc sql noprint;
                select left(put(count(*),8.)) into :exist
                from dictionary.columns
                where libname="&&LIBNAME_" and
                memname="&&MEMNAME_" and
                upcase(name)="&&CVAR";
                quit;
 
            %IF &EXIST=1 %THEN %LET NEW&TYPE=&&NEW&TYPE &CVAR;
            %ELSE %IF &EXIST NE 1 %THEN %LET NEW&TYPE=&&NEW&TYPE;
 
            %IF &EXIST=0 %THEN %DO;
               %PUT WARNING: &CVAR in &TYPE list does not exist in data set &DATA..;
               %PUT WARNING: &CVAR will not be analyzed.;
            %END;
 
        %END;
 
        %LET &&TYPE.=&&NEW&TYPE;
 
    %MEND;
 
    %VEXIST(LIST=&CAT1, TYPE=CAT1);
    %VEXIST(LIST=&CAT2, TYPE=CAT2);
    %VEXIST(LIST=&CON1, TYPE=CON1);
    %VEXIST(LIST=&CON2, TYPE=CON2);
    %VEXIST(LIST=&CON3, TYPE=CON3);
    %VEXIST(LIST=&ORD1, TYPE=ORD1);
 
    ***** ELIMINATE VARIABLES WITH COMPLETE MISSINGNESS *****;
    %MACRO NEWLIST(DSET, list);
        %LOCAL LIST LIST2 COND NEWCAT2 ICT2 CTVAR NEXVAR OKCAT2 CAT2;
 
        %LET LIST=%UPCASE(&LIST);
        %LET LIST2=%str(&)&list;
 
 
        %IF (&CLASS= ) %THEN %LET COND= ;
         %ELSE %LET COND=%str( where missing(___class)=0;);
 
 
        %IF &LIST=CAT2 %THEN %DO;
 
           %LET NEWCAT2= ;
           %LET ICT2=1;
           %LET CTVAR=%SCAN(&CAT2,&ICT2);
           %LET NEXVAR=&CTVAR;
           %DO %WHILE(&NEXVAR NE );
 
             %LET CTVAR=&NEXVAR;
             %LET ICT2=%EVAL(&ICT2+1);
             %LET NEXVAR=%SCAN(&CAT2,&ICT2);
 
             %LET OKCAT2= ;
             proc freq data=&DSET noprint;
               &COND;
               table &CTVAR/ out=____cout;
             run;
             proc means noprint data=____cout sum;
               var percent; output out=____s1 sum=_s;
             run;
             data _null_;
               set ____s1;
               if _s=. then ok=0; else ok=1;
               call symput('OKCAT2',put(ok,1.0));
               if ok=0 then do;
                  put
             "WARNING: Variable &CTVAR in list CAT2 is completely missing.";
                  put "WARNING: &CTVAR will not be analyzed.";
                end;
              run;
             %IF &OKCAT2=1 %THEN %LET NEWCAT2=&NEWCAT2 &CTVAR;
             proc datasets nolist; delete ____cout ____s1; run; quit;
            %END;
 
           %LET CAT2=&NEWCAT2;
         %END;
        %ELSE %IF (&LIST=CON1) or (&LIST=CON2) or (&LIST=CON3)
        or (&LIST=CAT1) or (&LIST=ORD1) %THEN %DO;
           proc means noprint data=&DSET n;
             &COND;
             var &LIST2;
             output out=_misout_  n=&LIST2;
           proc transpose data=_misout_ out=_Tmiss_; run;
           data _tmiss_;
             set _tmiss_;
             if _NAME_ ne '_TYPE_'; if _NAME_ ne '_FREQ_';
            run;
 
           data _null_;
              ** Get enough variable length for _vlist_ **;
             retain _vlist_ "&CON1 &CON2 &CON3 &CAT1 &ORD1  ";
             if _N_=1 then _vlist_=" ";
             retain change 0;
             set _Tmiss_ end=eof;
             if col1 ne 0 then _vlist_=trim(left(_vlist_))||" "||_NAME_;
             if col1=0 then do;
                change=1;
                put
        "WARNING: Variable " _NAME_ "in list &LIST is completely missing.";
                put "WARNING: " _NAME_ "will not be analyzed.";
               end;
             if eof=1 then do;
               if change=1 then call symput("&LIST", trim(_vlist_));
              end;
            run;
        %END;
 
    %MEND NEWLIST;
 
    %IF (&CON1 NE ) %THEN %DO; %NEWLIST(dset=_tempd_, list=CON1); %END;
    %IF (&CON2 NE ) %THEN %DO; %NEWLIST(dset=_tempd_, list=CON2); %END;
    %IF (&CON3 NE ) %THEN %DO; %NEWLIST(dset=_tempd_, list=CON3); %END;
    %IF (&CAT1 NE ) %THEN %DO; %NEWLIST(dset=_tempd_, list=CAT1); %END;
    %IF (&CAT2 NE ) %THEN %DO; %NEWLIST(dset=_tempd_, list=CAT2); %END;
    %IF (&ORD1 NE ) %THEN %DO; %NEWLIST(dset=_tempd_, list=ORD1); %END;
 
    ***** COUNT NUMBER OF VARIABLES *****;
    %LET NMCON1=0; %LET NMCON2=0; %LET NMCON3=0;
    %LET NMCAT1=0; %LET NMCAT2=0;
    %LET NMORD1=0;
    %LET NMLIST=0;
    %LET NOMORE=0; %LET COUNT=0;
 
    %DO %UNTIL (&NOMORE=1);
        %IF &NMCON1=0 %THEN %LET ICON1=&COUNT;
        %IF &NMCON2=0 %THEN %LET ICON2=&COUNT;
        %IF &NMCON3=0 %THEN %LET ICON3=&COUNT;
        %IF &NMCAT1=0 %THEN %LET ICAT1=&COUNT;
        %IF &NMCAT2=0 %THEN %LET ICAT2=&COUNT;
        %IF &NMORD1=0 %THEN %LET IORD1=&COUNT;
        %IF &NMLIST=0 %THEN %LET ILIST=&COUNT;
 
        %LET COUNT=%EVAL(&COUNT + 1);
        %LET CON1V=%SCAN(&CON1, &COUNT);
        %LET CON2V=%SCAN(&CON2, &COUNT);
        %LET CON3V=%SCAN(&CON3, &COUNT);
        %LET CAT1V=%SCAN(&CAT1, &COUNT);
        %LET CAT2V=%SCAN(&CAT2, &COUNT);
        %LET ORD1V=%SCAN(&ORD1, &COUNT);
        %LET LISTV=%SCAN(&LIST, &COUNT);
 
        %IF (&CON1V= ) %THEN %LET NMCON1=1;
        %IF (&CON2V= ) %THEN %LET NMCON2=1;
        %IF (&CON3V= ) %THEN %LET NMCON3=1;
        %IF (&CAT1V= ) %THEN %LET NMCAT1=1;
        %IF (&CAT2V= ) %THEN %LET NMCAT2=1;
        %IF (&ORD1V= ) %THEN %LET NMORD1=1;
        %IF (&LISTV= ) %THEN %LET NMLIST=1;
        %LET TOTNM = %EVAL(&NMCON1 + &NMCON2 + &NMCON3 + &NMCAT1 +
                           &NMCAT2 + &NMORD1 + &NMLIST);
        %IF (&TOTNM=7) %THEN %LET NOMORE=1;
    %END; *** end to DO UNTIL;
 
    ***** Check at least one variable was specified *****;
    %LET TOTVARN = %EVAL(&ICON1+&ICON2+&ICON3+&ICAT1+&ICAT2+&IORD1);
    %IF &TOTVARN=0 %THEN %DO;
        %PUT ERROR - No variables were specified for summarization.;
        %LET ERRORFLG=1;
    %END;
 
    ***** Check all continuous variables are SAS numeric *****;
    data _null_;
        set _tempd_;
        if _N_=1;
        badvar=0;
        %DO LOOP=1 %TO &ICON1;
            if vtype(%scan(&CON1, &LOOP))='C' then do;
                badvar=1;
                put
            "ERROR - %scan(&CON1, &LOOP) is not numeric, but was listed in CON1";
            end;
        %END;
        %DO LOOP=1 %TO &ICON2;
            if vtype(%scan(&CON2, &LOOP))='C' then do;
                badvar=1;
                put
             "ERROR - %scan(&CON2, &LOOP) is not numeric, but was listed in CON2";
             end;
        %END;
        %DO LOOP=1 %TO &ICON3;
            if vtype(%scan(&CON3, &LOOP))='C' then do;
                badvar=1;
                put
            "ERROR - %scan(&CON3, &LOOP) is not numeric, but was listed in CON3";
            end;
        %END;
        %DO LOOP=1 %TO &ICAT1;
            if vtype(%scan(&CAT1, &LOOP))='C' then do;
                badvar=1;
                put
            "ERROR - %scan(&CAT1, &LOOP) is not numeric, but was listed in CAT1";
            end;
        %END;
        %DO LOOP=1 %TO &IORD1;
            if vtype(%scan(&ORD1, &LOOP))='C' then do;
                badvar=1;
                put
            "ERROR - %scan(&ORD1, &LOOP) is not numeric, but was listed in ORD1";
            end;
        %END;
        if badvar=1 then call symput('ERRORFLG', left(put(badvar, 2.0)));
    run;
 
 
    *** MAKE SURE VARIABLES WERE NOT LISTED TWICE ***;
    data _tmpchk_;
        attrib _var length=$32;
        %DO LOOP=1 %TO &ICON1;
            _var="%upcase(%scan(&con1, &LOOP))"; output;
        %END;
        %DO LOOP=1 %TO &ICON2;
            _var="%upcase(%scan(&con2, &LOOP))"; output;
        %END;
        %DO LOOP=1 %TO &ICON3;
            _var="%upcase(%scan(&con3, &LOOP))"; output;
        %END;
        %DO LOOP=1 %TO &ICAT1;
            _var="%upcase(%scan(&cat1, &LOOP))"; output;
        %END;
        %DO LOOP=1 %TO &ICAT2;
            _var="%upcase(%scan(&cat2, &LOOP))"; output;
        %END;
        %DO LOOP=1 %TO &IORD1;
            _var="%upcase(%scan(&ord1, &LOOP))"; output;
        %END;
    run;
 
    proc freq noprint;
        table _var / out=_tmpout_;
    run;
 
    data _null_;
        set _tmpout_;
        if count>1 then do;
            put 'ERROR - A variable was listed more than once. ' _var= count= ;
            dummy=1;
            call symput('ERRORFLG', left(put(dummy, 2.0)));
        end;
    run;
 
    proc datasets nolist; delete _tmpchk_ _tmpout_ _misout_ _tmiss_; run; quit;
 
    ***** MAKE SURE THE SDOPT OPTION IS VALID *****;
    %IF (&SDOPT^=1) AND (&SDOPT^=2) %THEN %DO;
        %PUT ERROR - SDOPT parameter must be 1 or 2.;
        %LET ERRORFLG=1;
    %END;
 
    ***** IF LIST IS SPECIFIED AND THE SORTBY OPTION IS NOT SPECIFIED PUT WARNING *****;
    %IF (&LIST^= ) AND (&SORTBY= ) %THEN %DO;
        %PUT WARNING - Variables were listed in the LIST parameter,
        but the SORTBY parameter is unspecified.;
        %PUT;
    %END;
 
%END; *** end to IF &ERRORFLG^=1 THEN DO (checks);
 
*********************************************************************;
*********************************************************************;
***************** If no errors, begin macros   **********************;
*********************************************************************;
*********************************************************************;
%IF &ERRORFLG=0 %THEN %DO;
 
    ***************************************;
    ***** Manage titles and footnotes *****;
    ***** If ODSMEMO=1, do not do     *****;
    ***************************************;
    %IF (&ODSMEMO^=1) %THEN %DO;
 
        ***** Store Footnotes *****;
        proc sql ;
            create table work._f as select *
            from dictionary.titles where type='F';
            reset noprint; quit;
 
        proc sql;
            reset noprint;
            select nobs into :FN from dictionary.tables
            where libname="WORK" & memname="_F";
        quit;
 
        %IF (&FN>=1) %THEN %DO FLOOP=1 %TO &FN;
            %LOCAL FOOT&FLOOP;
        %END;
 
        ***** Store footnotes in macro variables *****;
        %LET FOOT1= ;** Initialize at least one title **;
        data _null_;
            set _f;
            %IF (&FN>=1) %THEN %DO FLOOP=1 %TO &FN;
                if number=&FLOOP then call symput("FOOT&FLOOP", trim(left(text)));
            %END;
        run;
 
        ***** If no base title stored, do not want to print "The SAS System" *****;
        proc sql noprint;
            select upcase(compress(text,  ,"scp")) into :CurrentTitle
            from  dictionary.titles
            where type="T" and number=1;
 
        %IF %SYMEXIST(CURRENTTITLE)=1 %THEN %DO;
            %IF &CURRENTTITLE=THESASSYSTEM %THEN %DO;
                title;
            %END;
        %END;
 
    %END;
 
    *********************************************************************;
    *************** Get number of classification levels  ****************;
    *********************************************************************;
    %IF (&ODSMEMO^=1) %THEN %DO; ods listing close; %END;
 
    proc freq data=_tempd_;
        table ___class / nocol norow nopercent;
        ods output OneWayFreqs=__groups;
    run;
 
    %IF (&ODSMEMO^=1) %THEN %DO; ods listing; %END;
 
    data _null_; set __groups nobs=_lev_ end=_eof;
        call symput('LEVELS', left(put(_lev_,2.)));
        if _eof then do;
            ctype=vtype(___class);
            call symput ('CLASSTYP',trim(left(ctype)));
        end;
    run;
 
    %DO LOOP=1 %TO &LEVELS;
        %LOCAL TVAL&LOOP TLAB&LOOP TNUM&LOOP;
    %END;
 
    data _null_;
        set __groups;
        retain  _tmlab1-_tmlab&LEVELS _tmn1-_tmn&LEVELS _tmv1-_tmv&LEVELS;
        length  _tmlab1-_tmlab&LEVELS $24;
        %DO LOOP=1 %TO &LEVELS;
            if _N_=&LOOP then do;
                %IF &CLASSTYP=C %THEN %DO;
                    _tmv&LOOP='"'||trim(left(___class))||'"';
                %END;
                %IF &CLASSTYP=N %THEN %DO;
                    _tmv&LOOP=trim(put(___class, 10.4));
                %END;
                _tmlab&LOOP = trim(left(F____class));
                _tmn&LOOP = trim(put(Frequency, 9.0));
            end;
            *** Assign levels and freqs to MACRO variables ***;
            call symput("TVAL&LOOP.", trim(left(_tmv&LOOP)));
            call symput("TLAB&LOOP.", trim(left(_tmlab&LOOP)));
            call symput("TNUM&LOOP.", trim(left(_tmn&LOOP)));
        %END;
     run;
 
    proc datasets nolist;
        delete __groups;
    run; quit;
 
    %IF (&CLASS= ) %THEN %PUT No class variable specified, producing overall summary.;
    %ELSE %IF (&CLASS^= ) %THEN %PUT The CLASS variable (&CLASS) has &LEVELS levels.;
    %PUT;
 
 
    %PUT ****** DISPLAY SUMMARY ******;
    %PUT CON1: &ICON1 VARIABLES;
    %PUT CON2: &ICON2 VARIABLES;
    %PUT CON3: &ICON3 VARIABLES;
    %PUT CAT1: &ICAT1 VARIABLES;
    %PUT CAT2: &ICAT2 VARIABLES;
    %PUT ORD1: &IORD1 VARIABLES;
    %PUT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
 
    ************************************************************************************;
    ********************** CATEGORICAL SUMMARY (R.L. version) **************************;
    ************************************************************************************;
    %LET ICAT=%EVAL(&ICAT1 + &ICAT2);
 
    %IF (&ICAT>0) %THEN %DO;
 
        %IF  (&PRINTP ne 1) AND (&ODSMEMO^=1) %THEN %DO; ods listing close; %END;
 
        ***** When Cutexact=0, use Exact tests only *****;
        ***** Otherwise. determine when to use Exact tests *****;
        %IF &CUTEXACT=0 %THEN %DO;
            %LET CatListExact = &CAT1 &CAT2 ;
        %END;
 
        %ELSE %IF &CUTEXACT ne 0 %THEN %DO;
            ***** Calculate expected counts for each cell *****;
            proc freq data=_tempd_;
               tables ___class*(&cat1 &cat2)/expected nopercent norow nocol sparse;
               ods output CrossTabFreqs=__expectedcounts;
            run;
 
            ***** Flag if less than 5 *****;
            data __expectedcounts;
               set __expectedcounts;
               if _type_="11";
               VarName=scan(table,3);
               ExpectedLess5=(Expected<5);
            run;
 
            ***** For each var calculate % of cells with expected values < 5 *****;
            proc freq data=__expectedcounts;
               tables varname*ExpectedLess5/nopercent nocol sparse;
               ods output CrossTabFreqs=__expectedless5;
            run;
 
            data __expectedless5;
               set __expectedless5(rename=(RowPercent=PctExpectedLess5));
               if _type_="11" and ExpectedLess5=1;
               keep varname PctExpectedLess5;
            run;
 
            ***** Create list of variables for Fisher Exact and list for Chi-square tests *****;
            *Anything in the __expectedless5 file? (will be empty if no var has expected count <5;
            proc sql noprint;
                select varname into :CheckList separated by ' '
                from __expectedless5;
 
            %IF %SYMEXIST(CheckList)=0 %THEN %DO;
                %LET CatListChiSq = &CAT1. &CAT2.;
            %END;
 
            %ELSE %IF %SYMEXIST(CheckList)=1 %THEN %DO;
                proc sql noprint;
                    select varname into :CatListExact separated by ' '
                    from __expectedless5 where PctExpectedLess5 ge &&cutexact.;
 
                proc sql noprint;
                    select varname into :CatListChiSq separated by ' '
                    from __expectedless5 where PctExpectedLess5 < &&cutexact.;
            %END;
        %END;  *** end to ELSE DO;
 
        ***** Get stats from PROC FREQ *****;
        %IF %SYMEXIST(CatListChiSq)=1 %THEN %DO;
            proc freq data=_tempd_   ;
            table ___class*(&CatListChiSq)
                / sparse
                %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;  chisq  %END;
                ;
 
               ***** Assign output to datasets ******;
               ods output CrossTabFreqs=_CTF1_
               %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                   ChiSq=_Chisq_;
               %END;
               ;
             run;
        %END;
 
        %IF %SYMEXIST(CatListExact)=1 %THEN %DO;
            proc freq data=_tempd_   ;
            table ___class*(&CatListExact)
                / sparse
                %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;  fisher %END;
                ;
 
               ***** Assign output to datasets ******;
               ods output CrossTabFreqs=_CTF2_
               %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                   FishersExact=_Fisher_
               %END;
               ;
             run;
        %END;
 
        %IF  (&PRINTP ne 1) AND (&ODSMEMO^=1) %THEN %DO; ods listing; %END;
 
        **** Put all CrossFreqs together ****;
        data _ctf_;
            set
               %IF %SYMEXIST(CatListChiSq)=1 %THEN %DO; _ctf1_ %END;
               %IF %SYMEXIST(CatListExact)=1 %THEN %DO; _ctf2_ %END;
               ;
        run;
 
        ***** Get p-values for each variable *****;
        %LET RMSTRING=Table ___class * ;
 
        %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
            %IF %SYMEXIST(CatListChiSq)=1 %THEN %DO;
               data _Chisq_;
                 attrib __var length=$32;
                 set _Chisq_;
                 keep _pval __var;
                 if Statistic='Chi-Square' then do;
                   __var = upcase(left(tranwrd(Table,"&RMSTRING"," ")));
                   _pval = Prob;
                   output;
                  end;
               run;
            %END;
 
            %IF %SYMEXIST(CatListExact)=1 %THEN %DO;
               data _Fisher_;
                 attrib __var length=$32;
                 set _Fisher_;
                 keep _pval __var;
                 if Name1='XP2_FISH' then do;
                   __var = upcase(left(tranwrd(Table,"&RMSTRING"," ")));
                   _pval = nValue1;
                   output;
                  end;
               run;
            %END;
 
            data _catpval_;
               set
               %IF %SYMEXIST(CatListChiSq)=1 %THEN %DO; _chisq_(in=inc) %END;
               %IF %SYMEXIST(CatListExact)=1 %THEN %DO; _fisher_(in=inf) %END;
               ;
               %IF %SYMEXIST(CatListExact)=1 %THEN %DO;
                 if inf then ExactTest='F';
                %END;
              %ELSE %DO;
                if inc then ExactTest=' ';
               %END;
            run;
        %END;
 
        **** Want to order rows as &CAT1 &CAT2 ****;
        data _order_;
            informat VarName $32.;
            %do LOOP=1 %to &ICAT;
                %let CATVARN=%scan(&CAT1 &CAT2, &LOOP);
                 VarName=upcase("&catvarn.");
                 _i=&loop;
                 output;
            %end;
        run;
 
        *Merge _i to _ctf_ and delete varname;
        proc sql;
            create table _ctf3_ as select *
            from _ctf_ a full join _order_ b
            on b.Varname=upcase(left(tranwrd(a.Table,"&RMSTRING"," ")));
 
        data _ctf_; set _ctf3_; drop varname; run;
 
        proc sort data=_ctf_; by _i &CAT1 &CAT2 ___class;
 
        %DO LOOP=1 %TO &ICAT; %LOCAL VF&LOOP; %END;
 
        ***** Get formats *****;
        data _null_; *** Get var. format ***;
            attrib _vf length=$50;
            set _ctf_;
            %DO LOOP=1 %TO &ICAT;
            %LET CATVARN=%SCAN(&CAT1 &CAT2, &LOOP);
                _vf = vformat(&catvarn);
                call symput("VF&LOOP", _vf);
            %END;
        run;
 
        ***** Get frequencies *****;
        data _FREQ;
            set _ctf_;
            by _i &CAT1 &CAT2 ___class;
            keep __n1-__n&LEVELS __n %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s %END;
            _m1-_m&LEVELS _s1-_s&LEVELS  _i _i2  _lab __var;
            length __var $32 _lab &LABLEN _vf $50;
            retain i 1 __n1-__n&LEVELS __n %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s %END;
            _m1-_m&LEVELS _s1-_s&LEVELS validobs _i2;
            array ns(&LEVELS)  __n1-__N&LEVELS;
            array m(&LEVELS)  _m1-_m&LEVELS;
            array s(&LEVELS)  _s1-_s&LEVELS;
 
            if first._i then i=1;
 
            %DO LOOP=1 %TO &ICAT; *** Get variable name ***;
                %LET CATVARN=%SCAN(&CAT1 &CAT2, &LOOP);
 
                if _i=&LOOP then do;
                    __var="%UPCASE(&CATVARN)";
                    _vf=vformat(&CATVARN);
 
 
                    ***** CAT2 SUMMARY *****;
                    if _i>&ICAT1 then do;
                        if  first.&CATVARN then do;
                            %IF &TOTALCOL=1 %THEN %DO; _m=.; _s=.; %END;
                            do j=1 to &LEVELS; m(j)=.; s(j)=.; end;
                            i=1;
                        end; ***end to if  first.&CATVARN then do;
                        if missing(___class)=0 and _type_='10' then do;
                            ns(i)=Frequency;
                            __n=sum(of __n1-__N&LEVELS);
                            i=i+1;
                            if last.&CATVARN then do;
                                _i2=_i;
                                call label(%SCAN(&CAT1 &CAT2, &LOOP), _lab);
                                output;
                            end; ***end to if last.&CATVARN then do;
                        end; ***end to if missing(___class)=0 and _type_='10';
                        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                            if missing(___class)=1 and _type_="01" then do;
                                _m=Frequency;
                                _s=Percent;
                            end; ***end of if missing(___class)=1 and;
                        %END; ***end of IF &TOTALCOL=1 THEN DO;
                        if missing(___class)=0 and _type_='11' then do;
                            m(i)=Frequency;
                            %IF &COLPCT=1 %THEN %DO;
                                s(i)=RowPercent;  **** sice class var is in the rows;
                            %END;
                            %ELSE %IF &COLPCT NE 1 %THEN %DO;
                                s(i)=ColPercent;  **** sice class var is in the rows;
                            %END;
                            i=i+1;
                            if last.&CATVARN then do;
                               _i2=_i2+0.01;
                               _lab=".     "||left(put(&CATVARN, &&VF&LOOP));
                               output;
                            end; ***end to if last.&CATVARN then do;
                        end; ***end to if missing(___class)=0 and _type_='11';
                    end;  ***end to if _i>&ICAT1 then do;
 
                    ***** CAT1 SUMMARY *****;
                    else if _i<=&ICAT1 then do;
                        if first._i then do;
                            do j=1 to &LEVELS; m(j)=.; s(j)=.; end;
                        end; ***end to if first._i then do;
                        if first.&CATVARN then i=1;
                        if missing(___class)=0 then validobs=1;
                        if missing(___class)=0 and _type_="11"  and &CATVARN=1 then do;
                            m(i) = Frequency;
                            %IF &COLPCT=1 %THEN %DO;
                                s(i)=RowPercent;  **** sice class var is in the rows;
                            %END;
                            %ELSE %IF &COLPCT NE 1 %THEN %DO;
                                s(i)=ColPercent;  **** sice class var is in the rows;
                            %END;
                            i=i+1;
                        end; ***end to if missing(___class)=0 and _type_="11" ;
                        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                            if missing(___class)=1 and _type_="01" and &CATVARN NE 1 and Percent=100 then do;
                                _m = 0;
                                _s = 0;
                            end;
                            else do;
                                if missing(___class)=1 and _type_="01" and &CATVARN=1 then do;
                                    _m = Frequency;
                                    _s = Percent;
                                end; ***end of if missing(___class)=1 and;
                            end;
                        %END; ***end of IF &TOTALCOL=1 THEN DO;
                        if missing(___class)=0 and _TYPE_='10' then do;
                            ns(i)=Frequency;
                            i=i+1;
                            __n=sum(of __n1-__N&LEVELS);
                        end; ***end to if missing(___class)=0 and _TYPE_='10';
                        if last._i;
                        call label(&CATVARN, _lab);
                        _i2=_i;
                        if validobs=1 and nmiss(of _m1-_m&LEVELS)=&LEVELS then do;
                            do j=1 to &LEVELS; m(j)=0; s(j)=0; end;
                            output;
                        end; ***end to if validobs=1 and nmiss;
                        else output;
                        validobs=.;
                   end; ***end to else if _i<=&ICAT1 then do;
 
                end; *** END to if _I=LOOP then do; ***;
            %END; *** END to DO LOOP=1 to ICAT ***;
        run;
 
        ***** Merge _freq and _catpval_ *****;
        %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
            proc sort data=_catpval_; by __var;
        %END;
        proc sort data=_freq; by __var; run;
 
        data _summ1_;
            attrib __var length=$32  _type length=$8;
            merge
            _freq(in=in2)
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                %IF %sysfunc(exist(_catpval_)) %THEN %DO;
                    _catpval_(in=in1)
                %END;
            %END; ;
            by __var;
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                if in1=0 then put 'No pvalue available for ' __var;
            %END;
            if in2=0 then put 'No Frequencies for ' __var;
            if in2;
            if _i<=&ICAT1 then _type='CAT1';
            if _i>&ICAT1 then _type='CAT2';
            _i2 = _i2 + &ICON1 + &ICON2 + &ICON3;
            keep __var _lab __n1-__n&LEVELS __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s %END;
            _m1-_m&LEVELS _s1-_s&LEVELS __n _type _i _i2
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO; _pval ExactTest %END; ;
        run;
 
        proc sort data=_summ1_ out=_summ1_(drop=_i);
            by _i _i2;
        run;
 
        ***** Delete dbs that are of no futher use *****;
        proc datasets nolist;
            delete
            _ctf_ _freq _ctf3_ _order_
            __expectedcounts __expectedless5
            %IF &LEVELS NE 1 %THEN %DO;  _catpval_  %END;
 
            %IF %SYMEXIST(CatListChisq)=1 %THEN %DO;
                _CTF1_
                %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO; _chisq_ %END;
            %END;
 
            %IF %SYMEXIST(CatListExact)=1 %THEN %DO;
                _CTF2_
                %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO; _fisher_ %END;
            %END;
            ;
        run; quit;
 
    %END;  ***end of IF (&ICAT>0);
 
    *********************************************************************;
    **************** CONTINUOUS ANALYSIS (R.L. version) *****************;
    *********************************************************************;
    %LET ICON=%EVAL(&ICON1 + &ICON2 + &ICON3);
 
    %IF (&ICON>0) %THEN %DO;
 
        %IF (&PRINTP ne 1) AND (&ODSMEMO^=1) %THEN %DO; ods listing close; %END;
 
        ***** Get summary stats *****;
        proc means data=_tempd_ n mean std median min max q1 q3 ;
            class ___class;
            var &con1 &con2 &con3;
            ods output Summary=_means_;
        run;
 
        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
            proc means data=_tempd_ n mean std median min max q1 q3 ;
                var &con1 &con2 &con3;
                ods output Summary=_meansall_;
            run;
        %END;
 
        ***** Get p-values *****;
        %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
 
            ***** ANOVA *****;
            %IF &ICON1>0 %THEN %DO;
                %DO AOVL=1 %TO &ICON1;
                    %LET NOWCON=%SCAN(&CON1,&AOVL);
                    proc npar1way data=_tempd_ anova;
                        class ___class;
                        var &nowcon;
                        ods output ANOVA=_anova__;
                    run;
                    %IF %sysfunc(exist(_anova__)) %THEN %DO;
                        data _anova__;
                            set _anova__;
                            attrib __var2 length=$32;
                            keep _i __var2 _pval;
                            if source='Among';
                            _pval=ProbF;
                            __var2="&NOWCON";
                            _var2=upcase(__var2);
                            _i=&AOVL;
                        run;
                        proc append base=_anova_ data=_anova__; run; quit;
                        proc datasets nolist; delete _anova__; run; quit;
                    %END;
                %END; *** end to DO AVOL=1;
                %IF %sysfunc(exist(_anova_)) %THEN %DO;
                    proc sort data=_anova_; by _i; run;
                %END;
            %END; *** end to IF &ICON1>0;
 
            ***** Kruskal-Wallis *****;
            %LET ICON23=%EVAL(&ICON2 + &ICON3);
 
            %IF &ICON23>0 %THEN %DO;
                %DO KWTL=1 %TO &ICON23;
                %LET NOWCON=%SCAN(&CON2 &CON3, &KWTL);
                    proc npar1way data=_tempd_ wilcoxon ;
                        class ___class;
                        var &nowcon;
                        ods output KruskalWallisTest=_kwt__;
                    run;
                    %IF %sysfunc(exist(_kwt__)) %THEN %DO;
                        data _kwt__;
                            set _kwt__;
                            attrib __var2 length=$32;
                            keep _i __var2 _pval;
                            if Label1='Pr > Chi-Square';
                            _pval=nValue1;
                            __var2="&NOWCON"; _var2=upcase(__var2);
                            _i=&ICON1 + &KWTL;
                        run;
                        proc append base=_kwt_ data=_kwt__; run; quit;
                        proc datasets nolist; delete _kwt__; run; quit;
                    %END; *** end to IF SYSFUNC;
                %END; ***end to DO KWTL;
               %IF %sysfunc(exist(_kwt_)) %THEN %DO;
                   proc sort data=_kwt_; by _i; run;
               %END;
            %END; *** end to IF ICON23;
 
 
        %END; *** end to IF LEVELS NE 1;
 
        %IF (&PRINTP ne 1) AND (&ODSMEMO^=1) %THEN %DO; ods listing; %END;
 
        ***** Var names and labels *****;
        data _labs;
            attrib __var1 length=$32 _lab length=&LABLEN;
            set _tempd_(obs=1);
            %DO LOOP=1 %TO &ICON;
                %LET CONVARN=%SCAN(&con1 &con2 &con3, &LOOP);
                __var1="&CONVARN";
                _lab=vlabel(&CONVARN);
                _i=&LOOP;
                output;
            %END;
            _var1=upcase(__var1);
            keep __var1 _lab _i;
        run;
 
        ***** Transpose output from proc means to get needed stats *****;
        proc transpose data=_means_ out=_tmeans_; run;
 
        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
            proc transpose data=_meansall_ out=_tmeansall_; run;
 
            data _tmeans_; set _tmeans_; _o=_n_;
 
            proc sort data=_tmeans_; by _name_ _label_;
            proc sort data=_tmeansall_; by _name_ _label_; run;
 
            data _tmeans1_;
                merge _tmeansall_(rename=(COL1=COL0))
                      _tmeans_;
                by _name_ _label_;
            run;
 
            proc sort data=_tmeans1_ out=_tmeans_(drop=_o); by _o; run;
        %END;
 
        ***** Get requested stats *****;
        data _tmeans_;
            set _tmeans_;
            if _LABEL_ in('N', 'Mean', 'Std Dev', 'Median',
            'Minimum', 'Maximum', 'Lower Quartile', 'Upper Quartile');
            retain _i 0 __n1-__n&LEVELS __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s _t %END;
            %DO LOOP=1 %TO &LEVELS; _m&LOOP _s&LOOP _t&LOOP %END; ;
            array ns(&LEVELS) __n1-__n&LEVELS; ***Ns;
            array m(&LEVELS) _m1-_m&LEVELS;    *** Mean or median ***;
            array s(&LEVELS) _s1-_s&LEVELS;    *** S.D. or min or Q1 ***;
            array t(&LEVELS) _t1-_t&LEVELS;    *** Max or Q3 ***;
            array c(&LEVELS) col1-col&LEVELS;
 
            if _LABEL_='N' then do;  *** Get N ***;
                _i=_i+1;  *** Placement among continuous variables ***;
                do i=1 to &LEVELS; ns(i)=c(i); end;
                __n=sum(of col1-col&LEVELS);
                do i=1 to &LEVELS;
                    m(i)=.; s(i)=.; t(i)=.;
                end;
            end; *** end to if _LABEL_;
            if _i<=&ICON1 then do;
                if _LABEL_='Mean' then do;  *** Get Mean ***;
                    %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                        _m=col0;
                    %END;
                    do i=1 to &LEVELS; m(i)=c(i); end;
                end;
                if _LABEL_='Std Dev' then do;   *** Get S.D. ***;
                    %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                        _s=col0;
                    %END;
                    do i=1 to &LEVELS; s(i)=c(i); end;
                    output;
                end;
            end; *** end to _i<=&ICON1;
            else do;
                if _LABEL_='Median' then do;  *** Get Median ***;
                    %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                        _m=col0;
                    %END;
                    do i=1 to &LEVELS; m(i)=c(i); end;
                end;
                if _i<=(&ICON1 + &ICON2) then do;
                    if _LABEL_='Minimum' then do; *** Get Minimum ***;
                        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                            _s=col0;
                        %END;
                        do i=1 to &LEVELS; s(i)=c(i); end;
                    end;
                    if _LABEL_='Maximum' then do; *** Get Maximum ***;
                        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                            _t=col0;
                        %END;
                        do i=1 to &LEVELS; t(i)=c(i); end;
                        output;
                    end;
                end; *** end to if _i<=(&ICON1 + &ICON2);
                else do;
                    if _LABEL_='Lower Quartile' then do; *** Get Q1 ***;
                        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                            _s=col0;
                        %END;
                        do i=1 to &LEVELS; s(i)=c(i); end;
                    end;
                    if _LABEL_='Upper Quartile' then do; *** Get Q3 ***;
                        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                            _t=col0;
                        %END;
                        do i=1 to &LEVELS; t(i)=c(i); end;
                        output;
                    end;
                end;
            end; *** end to else do;
            keep _name_ __n1-__n&LEVELS __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s _t %END;
            _m1-_m&LEVELS _s1-_s&LEVELS _t1-_t&LEVELS _i;
        run;
 
        proc sort data=_labs; by _i;
        proc sort data=_tmeans_; by _i; run;
 
        ***** Final prep and assign var type *****;
        data _summ2_;
            attrib __var length=$32 _type length=$8;
            merge _labs(in=in1) _tmeans_(in=in2)
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                %IF %sysfunc(exist(_anova_)) %THEN %DO; _anova_(in=in3) %END;
                %IF %sysfunc(exist(_kwt_)) %THEN %DO; _kwt_(in=in4)  %END;
            %END;
            ;
            by _i;
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                if not(in1=in2) then
                put 'WARNING! Mismatched merge in _summ2_ '
                in1= in2= __var1 _name_;
                if max(in3,in4)=1 then do;
                    if in1=0 or in2=0 then
                    put 'WARNING! Mismatched merge in _summ2_ '
                    in1= in2= in3= in4= __var2;
                    __var1=compress(upcase(__var1)); __var2=compress(upcase(__var2));
                    if in1=1 and ( __var1 ne __var2 ) then
                    put 'WARNING! Mismatched merge in _summ2_ '
                   __var1= __var2= _name_= ;
                end;
            %END;
            %ELSE %DO;
                if in1 ne in2 then
                put 'WARNING! Mismatched merge in _summ2_'
                in1= in2= __var1 _name_ ;
            %END;
            if in1 or in2
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO; or in3 or in4 %END; ;
            __var=upcase(__var1);
            if _i<=&ICON1 then _type='CON1';
            else if _i<=(&ICON1 + &ICON2) then _type='CON2';
            else _type='CON3';
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                if _type='CON1' and in3=0 then
                put 'No ANOVA F-test available for ' __var;
                %IF &LEVELS=2 %THEN %DO;
                    if _type in('CON2' 'CON3') and in4=0 then
                    put 'No Mann-Whitney-Wilcoxon test available for ' __var;
                %END;
                %ELSE %DO;
                    if _type in('CON2' 'CON3') and in4=0 then
                    put 'No Kruskal-Wallis test available for ' __var;
                %END;
            %END;
            _i2=_i;  *** Placement among ALL variables ***;
            keep __var _lab __n1-__n&LEVELS __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s _t %END;
            _m1-_m&LEVELS _s1-_s&LEVELS _t1-_t&LEVELS _type _i2
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO; _pval %END; ;
        run;
 
        ***** Delete dbs that are of no further use *****;
        proc datasets nolist;
            delete _labs _tmeans_ _means_
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                _meansall_  _tmeansall_ _tmeans1_
            %END;
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                %IF %sysfunc(exist(_anova_)) %THEN %DO; _anova_ %END;
                %IF %sysfunc(exist(_kwt_)) %THEN %DO; _kwt_ %END;
            %END; ;
        run; quit;
 
    %END; *** end to IF (&ICON>0) THEN DO;
 
    *********************************************************************;
    ***************** ORDINAL ANALYSIS (R.L. version) *******************;
    *********************************************************************;
    %IF &IORD1>0 %THEN %DO;
 
        %IF (&PRINTP ne 1) AND (&ODSMEMO^=1) %THEN %DO; ods listing close; %END;
 
        ***** Get stats from PROC FREQ *****;
        proc freq data=_tempd_   ;
            table ___class*(&ORD1)
            / sparse;
            ods output CrossTabFreqs=_CTF_;
        run;
 
        ***** Get pvalues *****;
        %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
            %DO KWTL=1 %TO &IORD1;
                %LET NOWORD=%SCAN(&ORD1, &KWTL);
 
                proc npar1way data=_tempd_ wilcoxon ;
                    class ___class;
                    var &noword;
                    ods output KruskalWallisTest=_kwt__;
                run;
 
                %IF %sysfunc(exist(_kwt__)) %THEN %DO;
                    data _kwt__; set _kwt__;
                        attrib __var length=$32;
                        keep __var _pval;
                        if Label1='Pr > Chi-Square';
                        _pval=nValue1;
                        __var="&NOWORD"; __var=upcase(__var);
                    run;
                    proc append base=_kwt_ data=_kwt__; run; quit;
                    proc datasets nolist; delete _kwt__; run; quit;
                %END;
            %END;
        %END;
 
        %IF (&PRINTP ne 1) AND (&ODSMEMO^=1) %THEN %DO; ods listing; %END;
 
        **** Order rows ****;
        data _ctf_; set _ctf_;
            retain _i 0;
            if table ne lag(table) then _i=_i+1;
        run;
        proc sort data=_ctf_; by _i &ORD1 ___class;
 
        ***** Get formats *****;
        %DO LOOP=1 %TO &IORD1; %LOCAL VF&LOOP; %END;
        data _null_; *** Get var. format ***;
             attrib _vf length=$16;
             set _ctf_;
             %DO LOOP=1 %TO &IORD1;
                 %LET ORDVARN=%SCAN(&ORD1, &LOOP);
                 _vf = vformat(&ordvarn);
                 call symput("VF&LOOP", _vf);
             %END;
        run;
 
        ***** Get frequencies *****;
        data _FREQS;
            set _ctf_;
            by _i &ORD1 ___class;
            keep __n1-__n&LEVELS __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s %END;
            _m1-_m&LEVELS _s1-_s&LEVELS __n _i _i2 _lab __var;
            length __var $32 _lab &LABLEN _vf $50;
            retain i 1 __n1-__n&LEVELS __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s %END;
            _m1-_m&LEVELS _s1-_s&LEVELS __n  _i2;
            array ns(&LEVELS)  __n1-__N&LEVELS;
            array m(&LEVELS)  _m1-_m&LEVELS;
            array s(&LEVELS)  _s1-_s&LEVELS;
 
            if first._i then i=1;
 
            %DO LOOP=1 %TO &IORD1; *** Get variable name ***;
                %LET ORDVARN=%SCAN(&ORD1, &LOOP);
                if _i=&LOOP then do;
                    __var="%UPCASE(&ORDVARN)";
                    _vf=vformat(&ORDVARN);
                    if  first.&ORDVARN then do;
                        %IF &TOTALCOL=1 %THEN %DO; _m=.; _s=.; %END;
                        do j=1 to &LEVELS; m(j)=.; s(j)=.; end;
                        i=1;
                    end;
                    if missing(___class)=0 and _type_='10' then do;
                        ns(i)=Frequency;
                        __n=sum(of __n1-__N&LEVELS);
                        i=i+1;
                        if last.&ORDVARN then do;
                            _i2=_i;
                            call label(&ORDVARN, _lab);
                            output;
                        end;
                    end;
                    %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                        if missing(___class)=1 and _type_="01" then do;
                            _m=Frequency;
                            _s=Percent;
                        end; ***end of if missing(___class)=1 and;
                    %END; ***end of IF &TOTALCOL=1 THEN DO;
                    if missing(___class)=0 and _type_='11' then do;
                        m(i)=Frequency;
                        %IF &COLPCT=1 %THEN %DO;
                            s(i)=RowPercent;  **** sice class var is in the rows;
                        %END;
                        %ELSE %IF &COLPCT NE 1 %THEN %DO;
                            s(i)=ColPercent;  **** sice class var is in the rows;
                        %END;
                        i=i+1;
                        if last.&ORDVARN then do;
                            _i2=_i2+0.01;
                            _lab=".     "||left(put(&ORDVARN, &&VF&LOOP));
                            output;
                        end;
                    end;
                end; *** END to if _I=LOOP then do; ***;
            %END; *** END to DO LOOP=1 to IORD1 ***;
        run;
 
        ***** Merge _freqs and _kwt_ *****;
        %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
            proc sort data=_kwt_; by __var;
        %END;
        proc sort data=_freqs; by __var; run;
 
        data _summ3_;
            attrib __var length=$32  _type length=$8;
            merge _freqs(in=in1)
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                %IF %sysfunc(exist(_kwt_)) %THEN %DO; _kwt_(in=in2) %END;
            %END;
            ;
            by __var;
            if in1=0 then put 'No Frequencies available for ' __var ;
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;
                %IF &LEVELS=2 %THEN %DO;
                    if in2=0 then
                    put 'No Mann-Whitney-Wilcoxon test available for ' __var ;
                %END;
                %ELSE %DO;
                if in2=0 then put 'No Kruskal-Wallis test available for ' __var;
                %END;
            %END;
            if in1;
            _type='ORD1';
            _i2 = _i2 + &ICON1 + &ICON2 + &ICON3 + &ICAT1 + &ICAT2;
            keep __var _lab __n1-__n&LEVELS __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; _m _s %END;
            _m1-_m&LEVELS _s1-_s&LEVELS __n _type _i _i2
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO;  _pval %END;
            ;
        run;
 
        proc sort data=_summ3_ out=_summ3_(drop=_i);
            by _i _i2;
        run;
 
        ***** Delete dbs that are of no further use *****;
        proc datasets nolist;
            delete _ctf_ _freqs
            %IF (&LEVELS NE 1 AND &PVALUES=1) %THEN %DO; _kwt_ %END; ;
        run; quit;
 
    %END; *** end of IF (&IORD>1) THEN DO;
 
    *********************************************************************;
    ***************** PREPARE RESULTS FOR PRINTING **********************;
    *********************************************************************;
    ***** If no ExactTest column created yet (no cat variables or not necessary),
          create a black one (will be called later on) *****;
    %IF &ICAT>0 %THEN %DO;
        data _null_;
            dsid=(open("_SUMM1_"));
            _exactexists_=(varnum(dsid, 'ExactTest')>0);
            call symput('EXACTEXISTS', left(put(_exactexists_, 2.)));
        run;
    %END;
    %ELSE %IF &ICAT=0 %THEN %LET EXACTEXISTS=0;
 
    ***** Combine all the summ files *****;
    data _fsumm_;
        set
          %IF &ICON>0  %THEN %DO; _summ2_ %END;
          %IF &ICAT>0  %THEN %DO; _summ1_ %END;
          %IF &IORD1>0 %THEN %DO; _summ3_ %END;
          ;
        %IF &LEVELS=1 OR &PVALUES NE 1 %THEN %DO; _pval=.; %END;
        %IF &EXACTEXISTS NE 1 %THEN %DO; ExactTest=" "; %END;
        label __var='Variable Name'
              __n='N'
              _type='Summary Type'
              _pval='P-value'
              _lab='Variable'
              %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                  _m="Total Statistic 1"
                  _s="Total Statistic 2"
                  %IF &ICON>&ICON1 %THEN %DO;
                     _t="Total Statistic 3"
                  %END;
              %END;
              %DO LOOP=1 %TO &LEVELS;
                  __n&LOOP="&&TLAB&LOOP N"
                  _m&LOOP="&&TLAB&LOOP Statistic 1"
                  _s&LOOP="&&TLAB&LOOP Statistic 2"
                  %IF &ICON>&ICON1 %THEN %DO;
                     _t&LOOP="&&TLAB&LOOP Statistic 3"
                  %END;
              %END;
              ;
    run;
 
    ***** If order list provided, create _list with order for sorting *****;
    %IF (&LIST^= ) %THEN %DO;
        data _list;
            attrib __var length=$32  _list length=8;
            keep __var _list;
            %DO LOOP=1 %TO &ILIST;
                __var=upcase("%SCAN(&LIST, &LOOP)");
                _list=&LOOP;
                output;
            %END;
        run;
 
       proc sort data=_list; by __var;
       proc sort data=_fsumm_; by __var; run;
 
       data &OUT;
           merge _fsumm_(in=inf) _list(in=inl);
           by __var;
           if inl>inf then put
           'WARNING! ' __var 'is in the LIST parameter, but was not analyzed.';
           if inf;
           if inl=0 then _list=&ILIST+1;
       run;
 
       proc sort data=&OUT; by _i2; run;
 
       proc datasets nolist; delete _list; run;
    %END;
 
    %ELSE %DO;
        data &OUT; set _fsumm_; _list=1; run;
    %END;
 
    ***** Delete Dbs that are of no futher use *****;
    proc datasets nolist;
        delete
          %IF &ICON>0  %THEN %DO; _summ2_ %END;
          %IF &ICAT>0  %THEN %DO; _summ1_ %END;
          %IF &IORD1>0 %THEN %DO; _summ3_ %END;
          _fsumm_
         ;
    run; quit;
 
    *********************************************************************;
    *****************    AD-HOC COMPARISONS (R.L)  **********************;
    *********************************************************************;
    %IF &LEVELS>2 and &PVALUES=1 and &ADHOC=1 %THEN %DO;
 
        ***** Get # of combinations and sig level *****;
        data _null;
             call symput('_NumComb', comb(&LEVELS, 2));
             call symput('_AdHocSig', 0.05/comb(&LEVELS, 2));
             call symput('_AdHocSigLabel', put(0.05/comb(&LEVELS, 2), pvaluef.));
        run;
 
        ***** Create list of sig. variables on which to do ad hoc testing *****;
        data AD;
            set &OUT;
            if floor(_i2)=_i2 and _pval<0.05;
            keep __var _type ExactTest;
        run;
 
        ***** Any observations in above ds? *****;
        proc sql noprint;
            select count(__var) into :NobsAD from AD;
 
        ***** If any overall test is significant, then proceed *****;
        %IF &NOBSAD > 0 %THEN %DO;
 
            ***** If lowest class level is coded as 0 create a new
            ___class + 1 var so that superscript and footnote
            numbers match in rtf *****;
            proc sql noprint;
                select min(___class) into :LowestL from _tempd_;
 
            data _tempd_;
                set _tempd_;
                %IF &LOWESTL=0 %THEN %DO;
                   ___class2=___class+1;
                %END;
                %ELSE %DO;
                   ___class2=___class;
                %END;
            run;
 
            ***** DB with distinct levels of class variable *****;
            proc sql noprint;
                create table DistinctLevels as
                select distinct ___class2 as _numlevel
                from _tempd_;
 
            ***** All choose 2 combinations *****;
            proc sql noprint;
                select v1._numlevel, v2._numlevel
                into :_AAD separated by ' ', :_BAD separated by ' '
                from DistinctLevels v1, DistinctLevels v2
                where v1._numlevel<v2._numlevel
                order by v1._numlevel, v2._numlevel;
 
            ***** List of cat variables on which to perfom
            ad-hoc chi-square tests *****;
            proc sql noprint;
                select __var into :CatListAD separated by ' '
                from AD where (_type="CAT1" or _type="CAT2") and ExactTest ne "F";
 
            ***** List of cat variables on which to perfom
            ad-hoc exact tests *****;
            proc sql noprint;
                select __var into :CatExactListAD separated by ' '
                from AD where (_type="CAT1" or _type="CAT2") and ExactTest="F";
 
            ***** List of con1 variables on which to perfom
            parametric ad-hoc tests *****;
            proc sql noprint;
                select __var into :Con1ListAD separated by ' '
                from AD where _type="CON1";
 
            ***** List of con2 and con3 variables on which to perfom
            nonparametric ad-hoc tests *****;
            proc sql noprint;
                select __var into :Con23ListAD separated by ' '
                from AD where _type in ("CON2" "CON3" "ORD1");
 
            ***** Run ad-hoc comparisons--loop through all combinations *****;
            %IF (&ODSMEMO^=1) %THEN %DO; ods listing close; %END;
 
            ***** Anova p-values for con1 variables *****;
            %IF (&CON1LISTAD^= ) %THEN %DO;
                data anova_adhoc; informat dependent $35.;
 
                ***** Number of variables to loop through *****;
                proc sql noprint;
                    select count(__var) into :NC1 from AD where _type="CON1";
 
                %DO __NUM=1 %TO &NC1;
                    %LET __CONVAR=%SCAN(&CON1LISTAD, &__NUM);
 
                    proc glm data=_tempd_;
                         class ___class2;
                         model &__CONVAR = ___class2;
                         lsmeans ___class2 / pdiff;
                         ods output Diff=ptemp;
                    run;
 
                    data anova_adhoc; set anova_adhoc ptemp; run;
 
                    proc datasets library=work; delete ptemp; run; quit;
               %END;
            %END;
 
            ***** Loop through all combinations for cat and con2 or con3 *****;
            %DO _ADLOOP=1 %TO &_NUMCOMB;
 
                %LET _A=%SCAN(&_AAD, &_ADLOOP);
                %LET _B=%SCAN(&_BAD, &_ADLOOP);
 
                ***** chi-square tests *****;
                %IF (&CATLISTAD^= ) %THEN %DO;
                  proc freq data=_tempd_;
                      where ___class2 in (&_A &_B);
                      tables ___class2*(&CATLISTAD)/nopercent nocol chisq;
                      ods output ChiSq=ChiSq;
                  run;
                %END;
 
                ***** Fishers excat tests *****;
                %IF (&CATEXACTLISTAD^= ) %THEN %DO;
                  proc freq data=_tempd_;
                      where ___class2 in (&_A &_B);
                      tables ___class2*(&CATEXACTLISTAD)/nopercent nocol fisher;
                      ods output FishersExact=ADFisher;
                  run;
                %END;
 
                ***** Get anova from above db *****;
                %IF (&CON1LISTAD^= ) %THEN %DO;
                  data Con1ps;
                      set anova_adhoc(rename=(_&_B.=p));
                      if input(RowName, 5.)=&_A.;
                      keep dependent p;
                  run;
                %END;
 
                ***** Kruskal-Wallis tests *****;
                %IF (&CON23LISTAD^= ) %THEN %DO;
                  proc npar1way data=_tempd_ wilcoxon;
                       where ___class2 in (&_A &_B);
                       class ___class2;
                       var &CON23LISTAD;
                       ods output WilcoxonTest=WSR;
                  run;
                %END;
 
                ***** Compile ps into 1 file *****;
                data ps_&_ADLOOP;
                    set
                    %IF (&CATLISTAD^= ) %THEN %DO;
                        chisq(in=inc where=(statistic='Chi-Square')
                        rename=(prob=p_&_A&_B))
                     %END;
                    %IF (&CATEXACTLISTAD^= ) %THEN %DO;
                        adfisher(in=inf where=(name1='XP2_FISH')
                        rename=(nvalue1=p_&_A&_B))
                     %END;
                    %IF (&CON23LISTAD^= ) %THEN %DO;
                        wsr(where=(name1='P2_WIL') rename=(nvalue1=p_&_A&_B))
                     %END;
                    %IF (&CON1LISTAD^= ) %THEN %DO;
                        con1ps(rename=(dependent=Variable p=p_&_A&_B))
                     %END;
                    ;
                    %IF (&CATLISTAD^= ) %THEN %DO;
                      if inc then Variable=scan(Table, 3);
                    %END;
                    %IF (&CATEXACTLISTAD^= ) %THEN %DO;
                      if inf then Variable=scan(Table, 3);
                    %END;
                    if p_&_A&_B<&_ADHOCSIG.; ***keep the significant comparisons;
                    keep variable p_&_A&_B;
                run;
 
                ***** sort for merging *****;
                proc sort data=ps_&_ADLOOP; by variable; run;
 
                ***** delete dbs for next loop *****;
                proc datasets library=work nolist;
                    delete chisq adfisher con1ps wsr lr;
                run; quit;
 
            %END; *** end to DO _ADLOOP=1 TO &_NUMCOMB;
 
            ***** Merge sig. ad hoc ps *****;
            data _ADPs;
                merge
                %DO LOOP=1 %TO &_NUMCOMB;
                    ps_&LOOP
                %END;
                ;
                by variable;
            run;
 
            ***** Merge these p-values to &out *****;
            proc sql;
                create table ADTemp as select *
                from &&OUT. a left join _ADPs b
                on a.__var=upcase(b.variable);
 
            ***** Drop variable *****;
            data &OUT; set ADTemp; drop variable; run;
 
            ***** Delete dbs that are of no further use *****;
            proc datasets library=work;
                delete ad DistinctLevels ps_1-ps_&_ADLOOP. _ADPs
                adtemp _NULL anova_adhoc;
            run; quit;
 
            %IF (&ODSMEMO^=1) %THEN %DO; ods listing; %END;
 
      %END; *** end to IF &NOBSAD > 0 THEN DO;
 
    %END; *** end to IF &LEVELS>2 and &ADHOC=1;
 
    *********************************************************************;
    *********************   Odds Ratios (R.L)  **************************;
    *********************************************************************;
    %IF &LEVELS=2 AND &ODDSRATIOS=1 %THEN %DO;
 
        %IF (&ODSMEMO^=1) %THEN %DO; ods listing close; %END;
 
        data _OR_; informat effect $36.;
 
        ***** CAT1 Variables *****;
        %IF &ICAT1>0 %THEN %DO;
            %DO ORCA1=1 %TO &ICAT1;
                %LET NOWCAT=%SCAN(&CAT1, &ORCA1);
 
                ***** Determine if cat1 var is coded 1/0 or 1/2
                to determine which level is ref *****;
                proc sql noprint;
                    select min(&NOWCAT), max(&NOWCAT) into :LowL, :MaxL
                    from _tempd_;
 
                proc logistic data=_tempd_;
                    class &NOWCAT /param=ref order=internal
                    %IF &LOWL=0 and &MAXL=1 %THEN %DO;
                        ref=first;
                    %END;
                    %IF &LOWL=1 and &MAXL=2 %THEN %DO;
                        ref=last;
                    %END;
                    model ___class(event=last) = &NOWCAT/clodds=wald;
                    ods output CLOddsWald=__ORs;
                    format ___class 8.;
                run;
 
                data _or_;
                    set _or_ __ors;
                run;
 
                proc datasets nolist; delete __ors; run; quit;
            %END; *** end to DO ORCA1=1;
        %END; *** end to IF &ICAT1>0;
 
        ***** CAT2 Variables *****;
        %IF &ICAT2>0 %THEN %DO;
            %DO ORCA2=1 %TO &ICAT2;
                %LET NOWCAT=%SCAN(&CAT2, &ORCA2);
 
                proc logistic data=_tempd_;
                    class &NOWCAT /param=ref ref=first order=internal;
                    model ___class(event=last) = &NOWCAT/clodds=wald;
                    ods output CLOddsWald=__ORs;
                    format ___class &NOWCAT 8.;
                run;
 
                data __ors; set __ors; _imerge=_n_+1;
 
                data _or_;
                    set _or_ __ors;
                run;
 
                proc datasets nolist; delete __ors; run; quit;
            %END; *** end to DO ORCA2=1;
        %END; *** end to IF &ICAT2>0;
 
        ***** CON1 Variables *****;
        %IF &ICON1>0 %THEN %DO;
            %DO ORCO1=1 %TO &ICON1;
                %LET NOWCON=%SCAN(&CON1, &ORCO1);
                %IF (&UNITSCON1 ^= ) %THEN %LET NOWUNT=%SCAN(&UNITSCON1, &ORCO1);
 
                proc logistic data=_tempd_;
                    model ___class(event=last) = &NOWCON/clodds=wald;
                    %IF (&UNITSCON1 ^= ) %THEN %DO;
                        units &NOWCON = &NOWUNT;
                    %END;
                    ods output CLOddsWald=__ORs;
                    format ___class 8.;
                run;
 
                data _or_;
                    set _or_ __ors;
                run;
 
                proc datasets nolist; delete __ors; run; quit;
            %END; *** end to DO ORCO1=1;
        %END; *** end to IF &ICON1>0;
 
        ***** CON2 Variables *****;
        %IF &ICON2>0 %THEN %DO;
            %DO ORCO2=1 %TO &ICON2;
                %LET NOWCON=%SCAN(&CON2, &ORCO2);
                %IF (&UNITSCON2 ^= ) %THEN %LET NOWUNT=%SCAN(&UNITSCON2, &ORCO2);
 
                proc logistic data=_tempd_;
                    model ___class(event=last) = &NOWCON/clodds=wald;
                    %IF (&UNITSCON2 ^= ) %THEN %DO;
                        units &NOWCON = &NOWUNT;
                    %END;
                    ods output CLOddsWald=__ORs;
                    format ___class 8.;
                run;
 
                data _or_;
                    set _or_ __ors;
                run;
 
                proc datasets nolist; delete __ors; run; quit;
            %END; *** end to DO ORCO2=1;
        %END; *** end to IF &ICON2>0;
 
        ***** CON3 Variables *****;
        %IF &ICON3>0 %THEN %DO;
            %DO ORCO3=1 %TO &ICON3;
                %LET NOWCON=%SCAN(&CON3, &ORCO3);
                %IF (&UNITSCON3 ^= ) %THEN %LET NOWUNT=%SCAN(&UNITSCON3, &ORCO3);
 
                proc logistic data=_tempd_;
                    model ___class(event=last) = &NOWCON/clodds=wald;
                    %IF (&UNITSCON3 ^= ) %THEN %DO;
                        units &NOWCON = &NOWUNT;
                    %END;
                    ods output CLOddsWald=__ORs;
                    format ___class 8.;
                run;
 
                data _or_;
                    set _or_ __ors;
                run;
 
                proc datasets nolist; delete __ors; run; quit;
            %END; *** end to DO ORCO3=1;
        %END; *** end to IF &ICON3>0;
 
        ***** ORD1 Variables *****;
        %IF &IORD1>0 %THEN %DO;
            %DO ORORD=1 %TO &IORD1;
                %LET NOWORD=%SCAN(&ORD1, &ORORD);
 
                proc logistic data=_tempd_;
                    model ___class(event=last) = &NOWORD/clodds=wald;
                    ods output CLOddsWald=__ORs;
                    format ___class 8.;
                run;
 
                data _or_;
                    set _or_ __ors;
                run;
 
                proc datasets nolist; delete __ors; run; quit;
            %END; *** end to DO ORORD=1;
        %END; *** end to IF &IORD1>0;
 
        ***** Format OR (95% CI) *****;
        data _or_;
            set _or_;
            if _n_>1;
            informat Variable $36. OR_CI $35.;
            Variable=upcase(scan(Effect,1));
            OR_CI=compress(put(OddsRatioEst, &f4.))||" ("||
                  compress(put(LowerCL, &f4.))||", "||
                  compress(put(UpperCL, &f4.))||")";
            if Unit ne 1 then OR_CI=trim(OR_CI)||"**";
            ORUnit=Unit;
            if _imerge ne . then _imerge=_imerge/100;
            if _imerge=. then _imerge=0;
            keep variable _imerge ORUnit OR_CI;
        run;
 
        ***** Merge to &Out *****;
        proc sql;
            create table _out1_ as select *
            from &&out a left join _or_ b
            on a.__var=b.variable and
            round(a._i2-floor(a._i2), 0.01)=b._imerge;
 
        proc sort data=_out1_
            out=&out(drop=_imerge variable);
            by _i2;
        run;
 
        %IF (&ODSMEMO^=1) %THEN %DO; ods listing; %END;
 
        ***** Delete dbs that are of no further use ****;
        proc datasets nolist;
            delete _or_ _out1_;
        run; quit;
 
 
    %END; *** end to IF &LEVELS=2 AND &ODDSRATIOS=1;
 
    *********************************************************************;
    ***************** SORT AND PRINT IF APPLICABLE **********************;
    *********************************************************************;
    ***** Sorty as requested *****;
    %IF (&SORTBY^= ) %THEN %DO;
        proc sort data=&OUT; by &SORTBY _i2; run;
    %END;
 
    %ELSE %DO;
        proc sort data=&OUT; by _i2; run;
    %END;
 
    ***** Clear results window *****;
    *dm 'odsresults' clear;
 
    ***** Print &out, if requested *****;
    %IF &PRINTS=1 %THEN %DO;
        proc print data=&out l;
            id __var;
            var  _lab _type __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                _m _s
                %IF (&ICON>&ICON1) %THEN %DO; _t %END;
            %END;
            %DO LOOP=1 %TO &LEVELS;
                _m&LOOP _s&LOOP
                %IF (&ICON>&ICON1) %THEN %DO; _t&LOOP %END;
            %END;
            %IF &LEVELS>1 AND &PVALUES=1 %THEN %DO; _pval %END; ;
            format _pval &pfmt
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                _m _s
                %IF (&ICON>&ICON1) %THEN %DO; _t %END;
            %END;
            _m1-_m&levels _s1-_s&levels
            %IF (&ICON>&ICON1) %THEN %DO;
                _t1-_t&levels
            %END;
            &perfmt;
        run;
    %END;
 
    *********************************************************************;
    *****************Create RTF file for printing (R.L)******************;
    *********************************************************************;
    ***** +/- sign for mean +/- sd *****;
    %let PlMi=%Str(±);
 
    ***** Total N to flag variables with missing values *****;
    %IF (&CLASS^=  ) %THEN %DO;
        proc sql noprint;
            select n(___class) into :TOTALN
            from _tempd_;
    %END;
    %ELSE %DO;
        %LET TOTALN = &&TNUM1.;
    %END;
    data _null_;
        call symput('TOTALN', trim(left(&TOTALN.)));
    run;
 
    ***** Create &RTFOut file *****;
    data &RTFOUT;
        set &OUT;
 
        ***** Create labels *****;
        _varlab=_lab; _lab2=_lab; _varlab2=_lab;
 
        if _i2=floor(_i2) then do;
            ***** _varlab will have flag for missing values *****;
            if &TOTALN ne __n then _varlab=trim(_lab)||"*";
            else _varlab=_lab;
 
            if _type= 'CON1' then do;
                if &SDOPT=1 then do;
                    _lab2=trim(_lab)||", Mean &PlMI. SD";
                    _varlab2=trim(_varlab)||", Mean &PlMI. SD";
                end;
 
                else do;
                    _lab2=trim(_lab)||", Mean (SD)";
                    _varlab2=trim(_varlab)||", Mean (SD)";
                end;
            end;
 
            else if _type= 'CON2' then do;
                _lab2=trim(_lab)||", Median (Min, Max)";
                _varlab2=trim(_varlab)||", Median (Min, Max)";
            end;
 
            else if _type= 'CON3' then do;
                _lab2=trim(_lab)||", Median (Q1, Q3)";
                _varlab2=trim(_varlab)||", Median (Q1, Q3)";
            end;
 
            else if _type in ('CAT1' 'CAT2' 'ORD1') then do;
                _lab2=trim(_lab)||", No. (%)";
                _varlab2=trim(_varlab)||", No. (%)";
            end;
        end; *** end to if _i2=floor(_i2) then do;
 
        ***** If requested, create column with missing *****;
        %IF &NCOL=0 AND (&MISSCOL=1 OR &MISSCOL=3) %THEN %DO;
            __missing=&TOTALN-__n;
        %END;
 
        %IF &NCOL=0 AND (&MISSCOL=2 OR &MISSCOL=3) %THEN %DO j=1 %TO &LEVELS;
            __missing&j=&&TNUM&j-__n&j;
        %END;
 
        ***** If Overall column requested, format stats *****;
        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
 
            informat Overall $30.;
 
            if _type = 'CAT1' or (_type in ('CAT2' 'ORD1') and _i2 ne floor(_i2)) then
            Overall= compress(put(_m, &f1) ||  ' (' ||
                              put(_s, &f3) || ')' ) ;
 
            if _type = 'CON1' then do;
 
                %IF &SDOPT=1 %THEN %DO;
                    Overall= compress(put(_m, &f2) || " &PlMi." ||
                                      put(_s, &f2)  ) ;
                %END;
 
                %ELSE %DO;
                    Overall= compress(put(_m, &f2) || " (" ||
                                      put(_s, &f2) || ')' ) ;
                %END;
 
            end;
 
            %IF (&ICON2>0) %THEN %DO;
                if _type = 'CON2' then
                Overall= compress(put(_m, &f2) || ' (' ||
                                  put(_s, &f2) || ', '||
                                  put(_t, &f2) || ')' ) ;
            %END;
 
            %IF (&ICON3>0) %THEN %DO;
                if _type = 'CON3' then
                Overall= compress(put(_m, &f2) || ' [' ||
                                  put(_s, &f2) || ', '||
                                  put(_t, &f2) || ']' ) ;
            %END;
 
        %END;
 
        ***** Summary for groups *****;
        %DO LLOOP=1 %TO &LEVELS;
 
            %IF &LEVELS>2 AND &ADHOC=1 %THEN %DO;
                informat A&LLOOP $50.;
            %END;
            %ELSE %DO;
                informat A&LLOOP $30.;
            %END;
 
            if _type = 'CAT1' or (_type in ('CAT2' 'ORD1') and _i2 ne floor(_i2)) then
            A&LLOOP= compress(put(_m&LLOOP, &f1) ||  ' (' ||
                              put(_s&LLOOP, &f3) || ')' ) ;
 
            if _type = 'CON1' then do;
 
                %IF &SDOPT=1 %THEN %DO;
                    A&LLOOP= compress(put(_m&LLOOP, &f2) || " &PlMi." ||
                                      put(_s&LLOOP, &f2)  ) ;
                %END;
 
                %ELSE %DO;
                    A&LLOOP= compress(put(_m&LLOOP, &f2) || " (" ||
                                      put(_s&LLOOP, &f2) || ')' ) ;
                %END;
 
            end;
 
            %IF (&ICON2>0) %THEN %DO;
                if _type = 'CON2' then
                A&LLOOP= compress(put(_m&LLOOP, &f2) || ' (' ||
                                  put(_s&LLOOP, &f2) || ', '||
                                  put(_t&LLOOP, &f2) || ')' ) ;
            %END;
 
            %IF (&ICON3>0) %THEN %DO;
                if _type = 'CON3' then
                A&LLOOP= compress(put(_m&LLOOP, &f2) || ' [' ||
                                  put(_s&LLOOP, &f2) || ', '||
                                  put(_t&LLOOP, &f2) || ']' ) ;
            %END;
 
            ***** If all missing in a group then change to ---- *****;
            if _type not in ('CAT2' 'ORD1') and __n&LLOOP=0 then A&LLOOP="----";
            else if _type in ('CAT2' 'ORD1') and _i2 ne floor(_i2) and __n&LLOOP=0 then A&LLOOP="----";
 
        %END; *** end to DO LLOOP=1 TO &LEVELS;
 
        ***** For cat2 and ord1 variables want N to appear on 1st line only;
        if _i2 ne floor(_i2) then __n=.;
 
        ***** For cat2 and ord1 variables want p on 1st line only *****;
        %IF &LEVELS>1  AND &PVALUES=1 %THEN %DO;
            if _i2=floor(_i2) then _p=_pval;
            else _p=.;
            if _i2 ne floor(_i2) then ExactTest=" ";
        %END;
 
        ***** For cat2 and ord1 variables want n on 1st line only *****;
        array _nloop_ __n1-__n&LEVELS __n __missing __missing1-__missing&LEVELS;
        if _type in ('CAT2' 'ORD1') then do over _nloop_;
            if _i2 ne floor(_i2) then _nloop_=.;
        end;
 
        ***** If PRINTPTYPE=1 then add superscripts to ps *****;
        %IF &LEVELS>1 AND &PVALUES=1 AND &PRINTPTYPE=1 %THEN %DO;
 
            informat _p2 $30.;
 
            if _p ne . then do;
 
                if _type='CON1' then
                _p2=compress(put(_p, &pfmt))||"^{super a}";
 
                if _type in ('CON2' 'CON3' 'ORD1') then
                _p2=compress(put(_p, &pfmt))||"^{super b}";
 
                if _type in ('CAT1' 'CAT2') and ExactTest=" " then
                _p2=compress(put(_p, &pfmt))||"^{super c}";
 
                if _type in ('CAT1' 'CAT2') and ExactTest="F" then
                _p2=compress(put(_p, &pfmt))||"^{super d}";
 
            end;
 
        %END;
 
        ***** If ad-hoc performed, flag sig. differences *****;
        %IF &LEVELS>2 and &PVALUES=1 and &ADHOC=1 %THEN %DO;
 
          %IF  &NOBSAD > 0 %THEN %DO; *** this one only exists if the above if then is true;
 
            ***** Initiate superscript columns *****;
            %DO LLLOOP=1 %TO &LEVELS;
                informat Super&LLLOOP $25.;
                Super&LLLOOP=" ";
            %END;
 
            %DO _ADLOOP=1 %TO &_NUMCOMB;
                %LET _A=%SCAN(&_AAD, &_ADLOOP);
                %LET _B=%SCAN(&_BAD, &_ADLOOP);
 
                ***since only the sig. ones are stored, can flag if not missing;
                if p_&_A&_B ne . then do;
                    Super&_A=compress(Super&_A||put(&_B, 1.));
                    Super&_B=compress(Super&_B||put(&_A, 1.));
                end;
            %END;
 
            ***** If appropriate, add supersript denoting sign. *****;
            %DO LP=1 %TO &LEVELS;
                if Super&LP ne " " then Super&LP="^{super "||compress(Super&LP)||"}";
                if Super&LP ne " " and ( _type not in ("CAT2" "ORD1") or
                (_type in ("CAT2" "ORD1") and scan(_i2,2,".")="01"))
                then A&LP=catx('',trim(A&LP),trim(Super&LP));
            %END;
 
          %END;
 
        %END;
 
        ***** If ORs requested, put reference in the first CAT2 level *****;
        %IF &ODDSRATIOS=1 %THEN %DO;
            if _type='CAT2' and scan(_i2,2,".")="01"
            and OR_CI=" " then OR_CI="reference";
        %END;
 
        ***** Data to store for %OUTSUMMTABLE USE *****;
        if _N_=1 then do;
            _nlev_ = &LEVELS;
            _NTotal_ = &TOTALN;
            %DO LLOOP=1 %TO &LEVELS;
                _label&LLOOP = "&&TLAB&LLOOP";
                _N&LLOOP = &&TNUM&LLOOP;
            %END;
            _ncol_=&NCOL;
            _misscol_=&MISSCOL;
            _subset_="&SUBSET.";
            _colpct_=&COLPCT;
            _adhoc_=&ADHOC;
        end;
 
        ***** empty column for use in proc report *****;
        _empty=' ';
 
        keep _lab2 __var _type
        __n1-__n&LEVELS __n
        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; Overall %END;
        A1-A&LEVELS
        %IF &LEVELS NE 1 AND &PVALUES=1 %THEN %DO;
            _p
            %IF &PRINTPTYPE=1 %THEN %DO;
                _p2
            %END;
        %END;
        %IF &ODDSRATIOS=1 %THEN %DO; OR_CI ORUnit %END;
        _i2 _list
        %IF &LEVELS NE 1 AND &PVALUES=1 %THEN %DO; _pval ExactTest %END;
        _lab _varlab _varlab2
        %IF &NCOL=0 AND (&MISSCOL=1 OR &MISSCOL=3) %THEN %DO; __missing %END;
        %IF &NCOL=0 AND (&MISSCOL=2 OR &MISSCOL=3) %THEN %DO;
            __missing1-__missing&LEVELS
        %END;
        _nlev_ _NTotal_ _label1-_label&LEVELS _N1-_N&LEVELS _empty
        _subset_ _colpct_ _ncol_ _misscol_ _adhoc_;
 
        label _lab2="Variable label 2"
              _varlab2="Variable label 2.2"
              _lab="Variable label 1"
              _varlab="Variable label 1.2"
              __n="Overall N"
              _i2="Input variable order"
              _list="List variable order"
              %IF &NCOL=0 AND (&MISSCOL=1 OR &MISSCOL=3) %THEN %DO;
                  __missing="Overall missing"
              %END;
              _nlev_="# of groups"
              _NTotal_="Sample size"
              _empty="Empty column for report"
              _subset_="Subset of population used"
              _colpct_="ColPct option for outsummtable"
              _ncol_="NCol option for outsummtable"
              _misscol_="MissCol option for outsummtable"
              _adhoc_="AdHoc option for outsummtable"
              %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                  Overall="Overall Summary"
              %END;
              %IF &LEVELS NE 1 AND &PVALUES=1 %THEN %DO;
                  _pval="p-value for sorting"
                  ExactTest="Exact test used"
              %END;
              %IF &LEVELS NE 1 AND &PVALUES=1 %THEN %DO;
                  _p="p-value"
                  %IF &PRINTPTYPE=1 %THEN %DO;
                      _p2="p-value w type"
                  %END;
              %END;
              %IF &ODDSRATIOS=1 %THEN %DO;
                  OR_CI="OR (95% CI)"
                  ORUnit="Units for OR"
              %END;
              %DO LOOP=1 %TO &LEVELS;
                  A&LOOP="&&TLAB&LOOP Summary"
                  _label&LOOP="Group &LOOP label"
                  _N&LOOP="&&TLAB&LOOP size"
                  %IF &NCOL=0 AND (&MISSCOL=2 OR &MISSCOL=3) %THEN %DO;
                      __missing&LOOP="&&TLAB&LOOP n missing"
                  %END;
              %END; ;
 
    run;
 
    ***** Order columns for easier navigation *****;
    data &RTFOUT;
        retain  _lab2 __var _type
        __n1-__n&LEVELS __n
        %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; Overall %END;
        A1-A&LEVELS
        %IF &LEVELS NE 1 AND &PVALUES=1 %THEN %DO;
            _p
            %IF &PRINTPTYPE=1 %THEN %DO;
                _p2
            %END;
        %END;
        %IF &ODDSRATIOS=1 %THEN %DO; OR_CI ORUnit %END;
        _i2 _list
        %IF &LEVELS NE 1 AND &PVALUES=1 %THEN %DO; _pval ExactTest %END;
        _lab _varlab _varlab2
        %IF &NCOL=0 AND (&MISSCOL=1 OR &MISSCOL=3) %THEN %DO; __missing %END;
        %IF &NCOL=0 AND (&MISSCOL=2 OR &MISSCOL=3) %THEN %DO;
            __missing1-__missing&LEVELS
        %END;
        _nlev_ _NTotal_ _label1-_label&LEVELS _N1-_N&LEVELS
        _subset_ _colpct_ _ncol_ _misscol_ _adhoc_ _empty;
        set &RTFOUT;
    run;
 
    ***** Print &RTFOUT if requested *****;
    %IF &PRINTS=2 %THEN %DO;
        proc print data=&RTFOUT l;
            id _lab;
            var   __n
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO; Overall %END;
            %DO LOOP=1 %TO &LEVELS;
                A&LOOP
            %END;
            %IF &LEVELS>1 AND &PVALUES=1 %THEN %DO; _p %END;  ;
            format
            %IF &LEVELS>1 AND &PVALUES=1 %THEN %DO; _p &pfmt  %END;
            __n __countf.;
        run;
    %END;
 
    *********************************************************************;
    *****************   Prepare Report for output  **********************;
    *********************************************************************;
    %IF (&RTFFILE^= ) OR (&XMLFILE^= ) OR (&PDFFILE^= ) OR (&ODSMEMO=1)
    %THEN %DO;
 
        ***** List with missing values to state on footnote if applicable *****;
        data _missing_;
            set &OUT;
            if __n ne &TOTALN and _i2 = floor(_i2);
            informat MissNote $35.;
            MissNote=trim(_lab)||" = "||compress(put(&TOTALN-__n, __countf.));
            keep MissNote;
        run;
 
        ***** macros variable with number of variables that have missing values *****;
        proc sql noprint;
            select count(MissNote) into :NumWMissing
            from _missing_;
 
        %IF &NCOL=0 AND &MISSCOL=0 AND &PRINTFN=1 AND &NUMWMISSING>0 %THEN %DO;
            ***** for each, 1 var that has label and # missing values
                  (this will be used in footnote) *****;
            proc sql noprint;
                 select MissNote into :MissingFootNote separated by ', '
                 from _missing_;
        %END;
 
        ***** If OR requested and units ne 1, create footnote *****;
        %IF &LEVELS=2 AND &ODDSRATIOS=1 AND
        ((&UNITSCON1^= ) OR (&UNITSCON2^= ) OR (&UNITSCON3^= ))
        %THEN %DO;
            data _orunits_;
                set &OUT;
                if _type in ('CON1' 'CON2' 'CON3') AND ORUnit NE 1;
                informat UnitNote $35.;
                UnitNote=trim(_lab)||" = "||compress(put(ORUnit, __countf.));
                keep UnitNote;
             run;
 
            proc sql noprint;
                select UnitNote into :ORUnitFootNote separated by ', '
                from _orunits_;
        %END;
 
        ***** Date and time for stamp of file *****;
        data _null_;
            datenow = today(); timenow=datetime();
            call symput('datenow', trim(left(put(datenow, weekdate.))));
            call symput('timenow', trim(right(put(timenow, tod5.2))));
        run;
 
        ***** Main footnote/title location
              (those specified with footnote and title SAS options) *****;
        %IF &BDYTITLE=1 %THEN %LET BD=bodytitle;
        %ELSE %LET BD=;
 
        ***** This option is needed so that the superscripts work *****;
        ods escapechar="^";
 
        ***** Paper orientation  *****;
        options orientation=&PAGE;
 
        ***** Define column width if not specified in call *****;
        %IF &ICON>&ICON1 AND &ADHOC=1 %THEN %LET CW2=275;
        %ELSE %IF &ICON le &ICON1 AND &ADHOC=1 %THEN %LET CW2=175;
        %ELSE %IF &ICON>&ICON1 AND &ADHOC=0 %THEN %LET CW2=200;
        %ELSE %LET CW2=125;
 
        %IF (&CWIDTH2=) %THEN %LET CWIDTH2=&CW2;
 
        %IF &DESCLABEL=0 %THEN %LET CW3=300;
        %ELSE %LET CW3=375;
 
        %IF (&CWIDTH3=) %THEN %LET CWIDTH3=&CW3;
 
        ***** Close listing to output window *****;
        %IF (&ODSMEMO^=1) %THEN %DO; ods listing close; %END;
 
        ***** Open ods destinations that have been requested *****;
        %IF (&RTFFILE^= ) %THEN %DO;
            ods rtf file=&RTFFILE  style=&STYLE &BD;
        %END;
 
        %IF (&XMLFILE^= ) %THEN %DO;
            %IF &BDYTITLE=0 %THEN %DO;
                ods tagsets.ExcelXP file=&XMLFILE
                style=&STYLE options(sheet_name="&TBLTITLE.");
            %END;
            %ELSE %IF &BDYTITLE=1 %THEN %DO;
                ods tagsets.ExcelXP file=&XMLFILE
                style=&STYLE options(sheet_name="&TBLTITLE."
                embedded_titles='yes' embedded_footnotes='yes');
            %END;
        %END;
 
        %IF (&PDFFILE^= ) %THEN %DO;
            ods pdf file=&PDFFILE  style=&STYLE
            title="&TBLTITLE." bookmarkgen=no bookmarklist=none;
        %END;
 
        %IF &ODSMEMO=1 %THEN %DO;
            ods rtf select all;
        %END;
 
        ***** Add footnotes if appropriate *****;
        ***** If ODSMemo = 1, do not do    *****;
        %IF (&ODSMEMO^=1) %THEN %DO;
 
            %IF (&FN>=1) %THEN %DO FLOOP=1 %TO &FN;
                footnote&FLOOP "&&FOOT&FLOOP";
            %END;
            %LET FPLUS1 = %EVAL(&FN + 1);
            %IF &DAYTIME=1 %THEN %DO;
                footnote&FPLUS1 h=10pt j=r "&timenow  &datenow";
            %END;
 
        %END;
 
        ***** Report *****;
        proc report data= &RTFOUT nowd
            style(report)={borderwidth=3 bordercolor=black cellpadding=3
                           font_size=11pt font_face=Times  FONTSTYLE= ROMAN}
 
            style(lines)={background=white foreground=black
                          font_size=9pt font_face=Times FONTSTYLE= ROMAN
                          protectspecialchars=off}
 
            style(column)={background=white foreground=black
                          font_size=11pt font_face=Times FONTSTYLE= ROMAN
                          font_weight=medium}
 
            style(header)={background=white foreground=black borderbottomstyle=double
                          font_weight=bold FONTSTYLE= ROMAN
                          font_size=11pt font_face=Times};
 
            column (
                   %IF &NCOL=0 and &MISSCOL=0 and &DESCLABEL=0 %THEN %DO;
                       ('^S={borderbottomcolor=white}' _varlab)
                   %END;
                   %ELSE %IF (&NCOL NE 0 or &MISSCOL NE 0) and &DESCLABEL=0 %THEN  %DO;
                       ('^S={borderbottomcolor=white}' _lab)
                   %END;
                   %ELSE %IF &NCOL=0 and &MISSCOl=0 and &DESCLABEL=1 %THEN %DO;
                       (_varlab2)
                   %END;
                   %ELSE %IF (&NCOL NE 0 or &MISSCOL NE 0) and &DESCLABEL=1 %THEN  %DO;
                       ('^S={borderbottomcolor=white}' _lab2)
                   %END;
                   %IF &LEVELS NE 1 AND &TOTALCOL NE 1 %THEN %DO;
                       %IF &NCOL=1 or &NCOL=3 %THEN %DO;
                           ('^S={borderbottomcolor=white}' __n)
                       %END;
                       %IF &NCOL=0 and (&MISSCOL=1 or &MISSCOL=3) %THEN %DO;
                           ('^S={borderbottomcolor=white}' __missing)
                       %END;
                   %END;
                   %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                       %IF (&NCOL=0 or &NCOL=2) AND (&MISSCOL=0 or &MISSCOL=2) %THEN %DO;
                           ('^S={borderbottomcolor=white}' Overall)
                       %END;
                       %ELSE %IF &NCOL=1 or &NCOL=3 %THEN %DO;
                           ("^S={borderbottomcolor=black borderbottomwidth=1}Overall/(N=&TOTALN.)"
                           __n Overall)
                           ('^S={borderbottomcolor=white}' _empty)
                       %END;
                       %ELSE %IF &NCOL=0 and (&MISSCOL=1 or &MISSCOL=3) %THEN %DO;
                           ("^S={borderbottomcolor=black borderbottomwidth=1}Overall/(N=&TOTALN.)"
                            __missing Overall)
                           ('^S={borderbottomcolor=white}' _empty)
                       %END;
                   %END;
                   %DO LOOP=1 %TO &LEVELS;
                       %IF (&NCOL=0 or &NCOL=1) AND (&MISSCOL=0 or &MISSCOL=1) %THEN %DO;
                           ('^S={borderbottomcolor=white}' A&LOOP)
                       %END;
                       %ELSE %IF &NCOL=2 or &NCOL=3 %THEN %DO;
                           ("^S={borderbottomcolor=black borderbottomwidth=1}&&TLAB&LOOP./(N=&&TNUM&LOOP.)"
                            __n&LOOP A&LOOP)
                           %IF &LEVELS NE 1 %THEN %DO;
                               ('^S={borderbottomcolor=white}' _empty)
                           %END;
                       %END;
                       %ELSE %IF &NCOL=0 and (&MISSCOL=2 or &MISSCOL=3) %THEN %DO;
                           ("^S={borderbottomcolor=black borderbottomwidth=1}&&TLAB&LOOP./(N=&&TNUM&LOOP.)"
                            __missing&LOOP A&LOOP)
                           %IF &LEVELS NE 1 %THEN %DO;
                               ('^S={borderbottomcolor=white}' _empty)
                           %END;
                       %END;
                   %END;
                   %IF &LEVELS>1 AND &PVALUES=1 %THEN %DO;
                       (/*%IF &NCOL NE 0 OR &MISSCOL NE 0 %THEN %DO;*/
                            '^S={borderbottomcolor=white}'
                        /*%END;*/
                       _p
                       %IF &PRINTPTYPE=1 %THEN %DO;
                           _p2 _pvalue
                       %END;
                       %ELSE %DO;
                            ExactTest _pvalue
                       %END;
                       )
                   %END;
                   %IF &ODDSRATIOS=1 %THEN %DO;
                       (/*%IF &NCOL NE 0 OR &MISSCOL NE 0 %THEN %DO;*/
                            '^S={borderbottomcolor=white}'
                        /*%END;*/
                        OR_CI)
                   %END;
                   );
 
            ***** Title *****;
            compute before _PAGE_ /style = {font_size=11pt font_face=Times
                                  FONTSTYLE=ROMAN font_weight=bold
                                  just=left borderbottomwidth=3
                                  borderbottomcolor=black bordertopcolor=white};
                line "&TBLTITLE";
            endcomp;
 
            ***** Variable name column *****;
            %IF &NCOL=0 and &MISSCOL=0 and &DESCLABEL=0 %THEN %DO;
                define _varlab /order = data "Factor"  left
                               style(header) = {just = left}
                               style(column) = {cellwidth = &CWIDTH3 font_weight=bold};
            %END;
 
            %ELSE %IF (&NCOL NE 0 or &MISSCOL NE 0) and &DESCLABEL=0 %THEN %DO;
                define _lab /order = data "Factor"  left
                            style(header) = {just = left bordertopcolor=white}
                            style(column) = {cellwidth = &CWIDTH3 font_weight=bold};
            %END;
 
            %IF &NCOL=0 and &MISSCOl=0 and &DESCLABEL=1 %THEN %DO;
                define _varlab2 /order = data "Factor"  left
                               style(header) = {just = left}
                               style(column) = {cellwidth = &CWIDTH3 font_weight=bold};
            %END;
 
            %ELSE %IF (&NCOL NE 0 or &MISSCOL NE 0) and &DESCLABEL=1 %THEN %DO;
                define _lab2 /order = data "Factor"  left
                             style(header) = {just = left bordertopcolor=white}
                             style(column) = {cellwidth = &CWIDTH3 font_weight=bold};
            %END;
 
            ***** N total or N missing columns if requested *****;
            %IF &LEVELS NE 1 AND &TOTALCOL=0 %THEN %DO;
                %IF &NCOL=1 OR &NCOL=3 %THEN %DO;
                    define __n / "N" format=__countf.
                               style(column)={just = center cellwidth = &CWIDTH1};
                %END;
 
                %IF &NCOL=0 and (&MISSCOL=1 or &MISSCOL=3) %THEN %DO;
                    define __missing / "N missing" format=__countf.
                                    style(column)={just = center cellwidth = &CWIDTH1};
                %END;
            %END;
 
            ***** Overall Summary if requested *****;
            %IF &LEVELS NE 1 AND &TOTALCOL=1 %THEN %DO;
                %IF (&NCOL=0 or &NCOL=2) AND (&MISSCOL=0 or &MISSCOL=2) %THEN %DO;
                    define Overall / display "Total/(N=&TOTALN.)"
                                   style(column)={just = center cellwidth = &CWIDTH2};
                %END;
                %ELSE %IF &NCOL=1 or &NCOL=3 %THEN %DO;
                    define __n / "N" format=__countf.
                              style(column)={just = center cellwidth = &CWIDTH1};
                    define Overall / display "Summary"
                                   style(column)={just = center cellwidth = &CWIDTH2};
                    define _empty / ' ' style(column)={cellwidth=1%};
 
                %END;
                %ELSE %IF &NCOL=0 and (&MISSCOL=1 or &MISSCOL=3) %THEN %DO;
                    define __missing / "N missing" format=__countf.
                              style(column)={just = center cellwidth = &CWIDTH1};
                    define Overall / display "Summary"
                                   style(column)={just = center cellwidth = &CWIDTH2};
                    define _empty / ' ' style(column)={cellwidth=1%};
                %END;
            %END;
 
            ***** Group summary stats *****;
            %DO LOOP=1 %TO &LEVELS;
                %IF (&NCOL=0 or &NCOL=1) AND (&MISSCOL=0 or &MISSCOL=1) %THEN %DO;
                    define A&LOOP / display "&&TLAB&LOOP./(N=&&TNUM&LOOP.)"
                                  style(column)={just = center cellwidth = &CWIDTH2
                                  protectspecialchars=off};
                %END;
                %ELSE %IF &NCOL=2 or &NCOL=3 %THEN %DO;
                    define __n&LOOP / display "n" format=__countf.
                                    style(column)={just = center cellwidth = &CWIDTH1};
                    define A&LOOP / display "Summary"
                                  style(column)={just = center cellwidth = &CWIDTH2
                                  protectspecialchars=off};
                    %IF &LEVELS NE 1 %THEN %DO;
                        define _empty / ' ' style(column)={cellwidth=1%};
                    %END;
                %END;
                %ELSE %IF &NCOL=0 and (&MISSCOL=2 or &MISSCOL=3) %THEN %DO;
                    define __missing&LOOP / display "n missing" format=__countf.
                                    style(column)={just = center cellwidth = &CWIDTH1};
                    define A&LOOP / display "Summary"
                                  style(column)={just = center cellwidth = &CWIDTH2
                                  protectspecialchars=off};
                    %IF &LEVELS NE 1 %THEN %DO;
                        define _empty / ' ' style(column)={cellwidth=1%};
                    %END;
                %END;
            %END; *** end to DO LOOP=1 TO &LEVELS;
 
            ***** p-value column if applicable *****;
            %IF &LEVELS>1 AND &PVALUES=1 %THEN %DO;
                define _p/display noprint;
                define  _pvalue / computed "p-value"
                                %IF &NCOL NE 0 OR &MISSCOL NE 0 %THEN %DO;
                                  style(header)={bordertopcolor=white}
                                %END;
                                style(column) = {just = center cellwidth = &CWIDTH1};
 
                %IF &PRINTPTYPE=1 %THEN %DO;
                    define _p2/display noprint;
                    compute _pvalue/character length=16;
                        _pvalue=_p2;
                        if _p < 0.05 then call define(_col_,
                        "style", "style=[font_weight=bold fontstyle=italic]");
                    endcomp;
                %END;
 
                %ELSE %DO;
                    define ExactTest/display noprint;
                    compute _pvalue /character length=12 ;
                          _pvalue=compress(put(_p, pvaluef.))||ExactTest;
                          if _p < 0.05 then call define(_col_,
                          "style", "style=[font_weight=bold fontstyle=italic]");
                    endcomp;
                %END;
            %END; *** end to IF &LEVELS>1 THEN DO;
 
            ***** OR column if requested *****;
            %IF &LEVELS=2 AND &ODDSRATIOS=1 %THEN %DO;
                define OR_CI/display "Odds Ratios/(95% CI)"
                            %IF &NCOL NE 0 OR &MISSCOL NE 0 %THEN %DO;
                              style(header)={bordertopcolor=white}
                            %END;
                            style(column)={just = center cellwidth = 200};
            %END;
 
            ***** Footnotes *****;
            %IF &NUMWMISSING>0 OR &PRINTFN=1 OR (&ADDFN^= ) OR &ADHOC=1 %THEN %DO;
                compute after/style(lines) = {just=left
                              borderbottomcolor=white bordertopcolor=black bordertopwidth=3};
 
                  %IF &NCOL=0 AND &MISSCOL=0 AND &PRINTFN=0 AND &NUMWMISSING>0  %THEN %DO;
                      line "*Data not available for all subjects.";
                  %END;
 
                  %IF &PRINTFN=1 %THEN %DO;
 
                      %IF (&SUBSET^= ) %THEN %DO;
                          line "Subset of population used: &SUBSET..";
                      %END;
 
                      %IF &LEVELS=1 OR &PVALUES NE 1 %THEN %DO;
                          %IF &NUMWMISSING>0 AND &NCOL=0 AND &MISSCOL=0 %THEN %DO;
                              line "*Data not available for all subjects. Missing values: &MissingFootNote..";
                          %END;
                          %IF &DESCLABEL=0 %THEN %DO;
                              %IF &COLPCT=1 %THEN %DO;
                                  line
    "Values presented as Mean &PlMi. SD, Median [P25, P75], Median (min, max) or N (column %).";
                              %END;
                              %ELSE %DO;
                                  line
    "Values presented as Mean &PlMi. SD, Median [P25, P75], Median (min, max) or N (row %).";
                              %END;
                          %END;
                      %END;
 
                      %ELSE %IF &LEVELS ge 2 AND &PVALUES=1 %THEN %DO;
                          %IF &NUMWMISSING>0 AND &NCOL=0 AND &MISSCOL=0 %THEN %DO;
                              line "*Data not available for all subjects. Missing values: &MissingFootNote..";
                          %END;
                          %IF &PRINTPTYPE=1 %THEN %DO;
                              %IF &DESCLABEL=0 %THEN %DO;
                                  %IF &COLPCT=1 %THEN %DO;
                                      line
    "Values presented as Mean &PlMi. SD, Median [P25, P75], Median (min, max) or N (column %).";
                                  %END;
                                  %ELSE %DO;
                                      line
    "Values presented as Mean &PlMi. SD, Median [P25, P75], Median (min, max) or N (row %).";
                                  %END;
                              %END;
                              line
    "p-values: a=ANOVA, b=Kruskal-Wallis test, c=Pearson's chi-square test, d=Fisher's Exact test.";
                          %END;
                          %ELSE %DO;
                              %IF &COLPCT=1 %THEN %DO;
                                  line
"Values presented as Mean &PlMi. SD with ANOVA, Median [P25, P75] or Median (min, max) with Kruskal-Wallis test, or N (column %) with Fi
sher's Exact test (F) or Pearson's chi-square test.";
                              %END;
                              %ELSE %DO;
                                  line
"Values presented as Mean &PlMi. SD with ANOVA, Median [P25, P75] or Median (min, max) with Kruskal-Wallis test, or N (row %) with Fishe
r's Exact test (F) or Pearson's chi-square test.";
                              %END;
                          %END;
                      %END; *** end to ELSE IF &LEVELS ge 2 THEN DO;
                  %END; *** end to IF &PRINTFN=1 AND &DESCLABEL=0;
 
                  %IF &ADHOC=1 AND &PVALUES=1 %THEN %DO;
                      %DO FNLOOP=1 %TO &LEVELS;
                          line "^{super &FNLOOP.}: Significantly different from &&TLAB&FNLOOP.";
                      %END;
                      line "A significance level of &_AdHocSigLabel. was used for pairwise ad-hoc comparisons.";
                  %END;
 
                  %IF &LEVELS=2 AND &ODDSRATIOS=1 AND %SYMEXIST(ORUNITFOOTNOTE)=1
                  %THEN %DO;
                      line "**Odds ratio for unit increase in variable: &ORUNITFOOTNOTE..";
                  %END;
 
                  %IF (&ADDFN^= ) %THEN %DO;
                    line "&ADDFN.";
                  %END;
 
                endcomp;
            %END;
 
        run;
 
        %IF (&ODSMEMO^=1) %THEN %DO; footnote&FPLUS1; %END;
 
        %IF (&RTFFILE^= ) %THEN %DO;
            ods rtf close;
        %END;
 
        %IF (&XMLFILE^= ) %THEN %DO;
            ods tagsets.ExcelXP close;
        %END;
 
        %IF (&PDFFILE^= ) %THEN %DO;
            ods pdf close;
        %END;
 
        ***** If ODSMEMO=1 then do not do following *****;
        %IF (&ODSMEMO^=1) %THEN %DO;
 
            ***** Rediret output to window again *****;
            ods listing;
 
            ***** Revert to portrait orientation  *****;
            options orientation=portrait;
 
        %END;
 
        proc datasets nolist lib=work;
            delete _missing_
            %IF &LEVELS=2 AND &ODDSRATIOS=1 AND
            ((&UNITSCON1= ) OR (&UNITSCON2= ) OR (&UNITSCON3= ))
            %THEN %DO;
                _orunits_
            %END;
            ;
        run; quit;
 
    %END; *** end to IF (&RTFFILE^= ) OR (&XMLFILE^= ) OR (&PDFFILE^= );
 
 
*********************************************************************;
*****************          End macros          **********************;
*********************************************************************;
***** Delete _f and _tempd_ datasets *****;
proc datasets nolist;
    delete _f _tempd_;
run; quit;
 
%END; *** end to IF &ERRORFLG=0 THEN DO at beginning ****;
 
%IF &ERRORFLG^=0 %THEN %DO;
    %PUT Errors exist in the macro call, no output will be produced.;
%END;
 
options validvarname=&VALID &dateon
%IF (&ODSMEMO^=1) %THEN %DO; quotelenmax %END; ;
 
%MEND;