*
;
/*
Subject: MACRO: SAS table ---> LaTeX file
   Date: 7 Oct 1997 15:13:50 GMT
   From: pat@po.CWRU.Edu (Paul A. Thompson)
Organization: Case Western Reserve University, Cleveland, OH (USA)
Newsgroups:   comp.soft-sys.sas


For those who use SAS and LaTeX, I have a macro which converts a SAS 
dataset into a LaTeX table.  It follows.  Enjoy.

-- 
Paul Thompson, Ph.D.     |  This is a coded .sig file.  Stare hard at
Department of Psychiatry |  the following terms, and tell me what you see:
Case Western Reserve Univ|  KJA:HJSDoJL*H**&o*&*S&kjalkJnUKJtL**a&&&r*&*.
Cleveland, OH  44106     |  JHJHBJSHeDHJGRHHoWUYoUYUdYWJNHJHoMNMwXJJ!YJY!

*/

***** SAS Macro  *****;
options nosource;

%* ****************************************************************** *;

%* Copyright Paul Thompson, Ph.D., 1996

%* Version 2.2 - 9/15/96

%* 
%* Report bugs and failures to 
%* pat@po.cwru.edu
%*

%* This macro system is wholely owned by Paul A. Thompson

%* 1. It may be used by anyone who retains this copyright notice

%* 2. It may be redistributed to anyone, provided this notice is

%*    also included

%* 3. It may not be sold or distributed in any manner other than

%*    totally free without the express written permission of

%*    Dr. Thompson

%* 4. If used for a commercial enterprise, notice must be given to

%*    Dr. Thompson

%* 5. The use of this macro is covered by the GNU copyleft agreement.  If

%*    any provisions indicated above are inconsistent with the GNU copyleft

%*    agreement, the GNU agreement is in effect rather than the provision

%*    above

%*

%* For the casual user - see macro _rectab below - this is

%* the primary driver for typesetting an intact rectangular table

%* The other macros are all support

%* 
%* How to learn to use this macro
%* 1.  Run example (remove macro comment specs first)
%* 2.  Read descriptions of parameters for _rectab
%* 3.  Try examples
%* ****************************************************************** *;

%* New in 2.2
%* 1. Support for sideways tables 
%* 2. Support for long tables
%* ****************************************************************** *;

%macro _itemcnt(_items=a b, _delim=1, _n=_nit);

%* ************************************************************ *;

%* This macro counts the number of items in a macro var

%* ************************************************************ *;

%let _delx=%str( ,*\~);%let &_n.=0;

%let _ksep=%quote(%substr(&_delx., &_delim., 1));

%do %while(%length(%scan(%str(&_items.),%eval(&&&_n.+1), %str(&_ksep.))) > 0);

%let &_n.=%eval(&&&_n. + 1); %end;

%mend _itemcnt;

%macro _fillp(_parm=_f, _tilda=1);

%put Just inside _fillp;

%* ****************************************************************** *;

%* _fillp - macro which unpacks information about multipliers

%* and builds up the correctly multiplied values

%*

%* Note to macro readers - the three & specification is correct

%* If you change it to 2 or 4, it will screw it up

%* That is because we call _fillp with the NAME of a macro, not

%* a macro argument.

%*

%* Protocol

%* _parm  - name of the macro variable with possible values to

%*          be unpacked

%* _tilda - whether the tilda separator is to be included

%*          in the repeated arguments - 1 yes 0 no

%*

%* Logic - Use @ specifications to find the mult + string

%*         Assume that multiplier is first two columns

%*         Keep the info before the repeated section as _fa

%*         Keep the info after  the repeated section as _fr

%*         Build up repeats in _fm

%*         The final result is _parm again

%* ****************************************************************** *;

%if (&_tilda.) %then %do; %let _b=1;%let _sstr=~@; %end;

%else                %do; %let _b=0;%let _sstr=@;  %end;

%put * ************************************************************** *;

%do %while(%index(&&&_parm.,&_sstr.));

%put  _fillp: &_parm[&&&_parm.] (before);

%let _fa=%substr(&&&_parm., 1, %eval(%index(&&&_parm.,&_sstr.)-1+&_b.));

%let _fx=%substr(&&&_parm.,    %eval(%index(&&&_parm.,&_sstr.)+3+&_b.));

%let _fcnt=%substr(&&&_parm.,  %eval(%index(&&&_parm.,&_sstr.)+1+&_b.), 2);

%put  _fillp: _fcnt[&_fcnt.] (multiplier);

%if (%length(&_fx.) > %index(&_fx.,@)) %then

%let _fr=%substr(&_fx.,       %eval(%index(&_fx.,@)+1));

%else %let _fr=;

%let _fb=%substr(&_fx., 1,    %eval(%index(&_fx.,@)-1));

%let _fm=;

%do _i=1 %to &_fcnt.;

%let _fm=&_fm.&_fb.;

%if (&_tilda.) %then %if (&_i. < &_fcnt.) %then %let _fm=&_fm.~;

%end;

%let &_parm=&_fa.&_fm.&_fr.;

%put  _fillp: &_parm[&&&_parm.] (after);

%end;

%put * ************************************************************** *;

%mend _fillp;

%macro _rectab(_byv=,

               _byhls=,

               _byntf=0,

               _byntv=,

               _byntfmt=,

               _capt=_NULL_,

               _filenm=,

               _filext=tex,

               _forms=,

               _hdr1=,_hdr2=,_hdr3=,_hdr4=,_hdr5=,

               _hdr6=,_hdr7=,_hdr8=,_hdr9=,_hdr10=,

               _label=,

               _ladja=,

               _ladjb=,

               _lineadh=,

               _locate=,

               _longtab=0,

               _inset=a,

               _ipr=0,

               _just=,

               _missv=0,

               _ncol=,

               _produce=2,

               _sep=/,

               _sideway=0,

               _strx=,

               _ststrx=,

               _submdir=/home/local/texsrc/paul,

               _subsdir=neurop,

               _tabenv=,

               _tabidnt=,

               _tabname=,

               _trl1=,_trl2=,_trl3=,_trl4=,_trl5=,

               _trl6=,_trl7=,_trl8=,_trl9=,_trl10=,

               _trwidth=,

               _vars=);

%* ****************************************************************** *;

%* _rectab                                                            *;

%* Sets up latex code for typesetting rectangular table               *;

%*                                                                    *;

%* Protocol                                                           *;

%* _byv      - Variable treated as by variable                        *;

%*             Value output when first.byv, otherwise blank           *;

%* _byhls    - # \hline to produce when BY-var isnew (>= 0)           *;

%* _byntf    - put new tables in new files (1-yes, 0-no)              *;

%* _byntv    - variable to make new tables with - treated as BY var   *;

%* _byntfmt  - format which is used (if exists) to add values of      *;

%*             _byntv to end of _capt and _tabidnt - if not set,      *;

%*             values are added in an unformated manner               *;

%* _capt     - caption for table (enclose in quotes)                  *;

%* _filenm   - file name for final table                              *;

%* _filext   - file extension for final table - .tex default          *;

%* _forms    - formats for setting up vars - use ~ as separator       *;

%*             If a variable is character, use $.                     *;

%* _hdr1-10  - variables with header information                      *;

%*             _S_ skips that column, and is a mandatory              *;

%*             placeholder                                            *;

%* _ipr      - internal printing - see forbidden things as the macro  *;

%*             does many disgusting things - prepare for total boredom*;

%* _locate   - table location (single letter - see Latex Manual)      *;

%* _longtab  - make as longtable                                      *;

%* _inset    - input dataset                                          *;

%* _just     - justification of each variable in vars - single letter *;

%*             see LaTeX manual                                       *;

%* _lineadj  - line adjustment string, extra vertical glue for one    *;

%*             TeXnical need or another                               *;

%* _missv    - If set to 1 and value is missing, generate a blank     *;

%* _produce  - 0-no output: 1-SAS print  2-LaTeX source  3-SAS/LaTeX  *;

%* _sep      - subdirectory separator - either "/" or "\" (DOS, UNIX) *;

%* _sideway  - make sideways table                                    *;

%* _strx     - strings for tabular listing - use ~ as separator.      *;

%*             Use _S_ as separator.  These strings are placed AFTER  *;

%*             column listing.                                        *;

%* _ststrx   - string placed at the very start of the tabular list.   *;

%* _submdir  - main subdirectory listing                              *;

%* _subsdir  - optional subdirectory listing from main                *;

%* _tabenv   - LaTeX command specifying table environment             *;

%*             Usually, size (\small) or spacing (\singlespace)       *;

%* _tabidnt  - table identification - passed as comment to LaTeX      *;

%* _tabname  - table name - set up as label for table                 *;

%*             See LaTeX manual                                       *;

%* _trl1-10  - trailer 1-10 - information placed in trailer lines     *;

%* _trwidth  - trailer width - used in parbox specification           *;

%* _vars     - listing of variables to be set up                      *;

%*

%* The separator for arguments is the tilda (~)

%*

%* Note: Two sorts of multipliers may be used in the specifications

%* of a table.

%* 1. In _forms, _just, _strx, _hdr1-_hdr10, the

%*    text duplicate multiplier may be used.  This is used as

%*       _forms=$~@034.@,  ---> _forms=$~4.~4.~4.,

%*    In this multiplier, "@" delimits the whole string, first

%*    2 chars are the multiplier count, and rest is the body.  These

%*    may NOT be nested.  They MUST be paired.
%* 2. In _hdr1-_hdr10, multicolumn designations are specified by using

%*    the # specifier.  If the 1st col is a #, the next two columns are

%*    the number of columns to be included in the multicolumn specifier

%*    _hdr1=#04Header line~Rest ---> \multicolumn{4}{c}{Header line} & Rest \\
%* ****************************************************************** *;

%*

     /*

%*  data chk;

%*  do k=1 to 3;

%*  do i=1 to 5;

%*  subj="Sub"||put(i,2.);

%*  array vls v1-v3;

%*  do j=1 to 3;

%*  vls{j}=uniform(98232)*3;

%*  end;

%*  output;

%*  end;

%*  end;

%*  run;

%*  data chk2;set chk(in=ina) chk(in=inb);

%*  tabv=ina+inb*2;run;

%*  proc format ;value gfmt 1="Group A" 2="Group B" 3="Group C";

%*               value tabvfmt 1="File with main"

%*                             2="Additional information";run;

%*  %_maktab;

%*  %macro _runit;

%*  %do _zzi=1 %to 2;

%*  %_rectab(_byv=k,

%*           _byhls=2,

%*  %if (&_zzi. = 1) %then %do;

%*           _capt=%str(Example table, random values),

%*           _filenm=redoa,

%*           _inset=chk,

%*  %end;

%*  %if (&_zzi. = 2) %then %do;

%*           _capt=%str(Separate tables:),

%*           _byntf=1,

%*           _byntv=tabv,

%*           _byntfmt=tabvfmt,

%*           _filenm=redob,

%*           _inset=chk2,

%*  %end;

%*           _filext=tex,

%*           _forms=gfmt.~$~@036.3@,

%*           _hdr1=#05Overall table header,

%*           _hdr2=_S_~Subject~#03Random Quantities,

%*           _hdr3=#02_S_~#03With values specified,

%*           _hdr4=_S_~_S_~Value 1~Value 2~Value 3,

%*           _locate=H,

%*           _just=rrccc,

%*           _missv=0,

%*           _ncol=,

%*           _sep=\,

%*           _sideway=0,

%*           _strx=,

%*           _submdir=c:\saswin,

%*           _subsdir=,

%*           _tabenv=\small,

%*           _tabidnt=_rectab example:full use,

%*           _tabname=tab:exmp,

%*           _trl1=Note: Information in this table defined,

%*           _trl2=according to Freedom of Information Act,

%*           _trl3=specifications,

%*           _trwidth=4in,

%*           _vars=k~subj~v1~v2~v3);

%*  %end;

%*  %mend  _runit;

%*  %_runit;

  /*     */

%* ****************************************************************** *;

%let _zepp=&_sep.;

%let _cntv=;

%let _cntbyv=;

%let _sw=;

%if (&_sideway. = 1) %then %let _sw=sideways;

%if (&_longtab. = 1) %then %let _sw=long;

%put Unpacking _forms....;

%_fillp(_parm=_forms, _tilda=1);

%put Unpacking _just....;

%_fillp(_parm=_just, _tilda=0);

%if  (%length(&_strx.) > 0) %then %do;

%put Unpacking _strx....;

%_fillp(_parm=_strx, _tilda=1);

%end;

%do _i=1 %to 10;

%if  (%length(&&_hdr&_i..)) %then %_fillp(_parm=_hdr&_i., _tilda=1);

%end;

%if (%length(&_byntfmt.)) %then %do;

%if (%substr(&_byntfmt.,%length(&_byntfmt.),1) ^= .) %then %let _byntfmt=&_byntfmt..;

%end;

%if (%length(&_ncol.)) %then %let _cntv=&_ncol.;

%else %_itemcnt(_items=&_vars., _n=_cntv, _delim=5);

%if (%length(&_byv.)) %then %_itemcnt(_items=&_byv., _n=_cntbyv, _delim=5);

%else %let _cntbyv=0; %let _bystr=;

%do _i=1 %to &_cntbyv.;

%let _bystr=&_bystr. %scan(&_byv., &_i., ~);%let _byhl&_i.=0;

%if (%length(&_byhls.)) %then %let _byhl&_i.=%scan(&_byhls.,&_i.,~);

%end;

%put **************************************************************** *;

%put _cntv[&_cntv.], _cntbyv[&_cntbyv.];

%do _i=1 %to &_cntbyv.;

%put _byhl&_i.[&&_byhl&_i..];

%end;

%put **************************************************************** *;

%do _j=1 %to &_cntv.;%let _byi&_j.=0;%end;

%do _i=1 %to &_cntbyv.;%let _byv&_i.=0;

%do _j=1 %to &_cntv.;

%if (%scan(&_bystr.,&_i.) = %scan(&_vars.,&_j.,~)) %then %do;

  %let _byv&_i.=1;%let _byi&_j.=1;%end;

%end;

%if (&&_byv&_i. = 0) %then %do;

%put ****** ERROR ****** ERROR ****** ERROR ****** ERROR ****** ERROR ******;

%put By variable %scan(&_bystr.,&_i.) is not found in the variables list;

%put This may be an error;

%put ****** ERROR ****** ERROR ****** ERROR ****** ERROR ****** ERROR ******;

%end;

%end;

%let _ext=0;

%if (%length(&_trl1.)) %then %let _ext=1;

%if (%length(&_bystr.)) %then %do;

PROC SORT DATA=&_inset. OUT=_TSET;

     BY &_byntv. &_bystr.;

RUN;

%end;

%else %do;

DATA _TSET;SET &_inset.;RUN;

%end;

%let _byntc=0;

%if (%length(&_byntv.)) %then %do;

PROC FREQ DATA=_TSET;TABLES &_byntv./NOPRINT OUT=NEWTAB;RUN;

DATA _NULL_;SET NEWTAB END=EOF;

IF (EOF) THEN CALL SYMPUT("_byntc",COMPRESS(PUT(_N_,4.)));RUN;

%end;

%put *************************************************************** *;

%put _byntc[&_byntc.];

%put *************************************************************** *;

PROC CONTENTS DATA=_TSET NOPRINT OUT=BYTYPE;RUN;

%do _j=1 %to &_cntv.; %let _mvt&_j.=0;%end;

DATA _NULL_;SET BYTYPE;

%do _j=1 %to &_cntv.;

     IF (UPCASE(NAME) = UPCASE("%scan(&_vars.,&_j.,~)")) THEN

        CALL SYMPUT(COMPRESS("_mvt&_j."), PUT(TYPE,1.));

%end;

RUN;

%if (&_ipr.) %then %do;

%put *************************************************************** *;

%do _j=1 %to &_cntv.;

%put _var[%scan(&_vars.,&_j.,~)], _mvt&_j.[&&_mvt&_j.];

%end;

%put *************************************************************** *;

%end;

DATA _XV;

     SET _TSET;

%if (&_cntbyv. > 0) %then %do;          BY &_byntv. &_bystr.;%end;

%else %if (%length(&_byntv.)) %then %do;BY &_byntv.;%end;

     LENGTH _V1-_V&_cntv. $80.;

     ARRAY FVLS _V1-_V&_cntv.;

     _POS=0;

%do _i=1 %to &_cntv.;

%if (&&_byi&_i.) %then %do;

%if (%scan(&_forms.,&_i.,~) = $) %then %do;

     IF FIRST.%scan(&_bystr., &_i.) THEN DO;

        FVLS{&_i.}=%scan(&_vars.,&_i.,~);

        CHKPOS=&&_byhl&_i.;

        _POS=MAX(OF _POS, CHKPOS);

     END;

     ELSE FVLS{&_i.}=" ";

%end;

%else %do;

%if (&&_mvt&_i. = 1) %then %do;

     IF (^FIRST.%scan(&_bystr., &_i.) |

         (&_missv. = 1 & %scan(&_vars.,&_i.,~) = .)) THEN FVLS{&_i.}=" ";

%end;

%if (&&_mvt&_i. = 2) %then %do;

     IF (^FIRST.%scan(&_bystr., &_i.) |

         (&_missv. = 1 & %scan(&_vars.,&_i.,~) = " ")) THEN FVLS{&_i.}=" ";

%end;

        ELSE DO;

           FVLS{&_i.}=PUT(%scan(&_vars.,&_i.,~), %scan(&_forms.,&_i.,~));

           CHKPOS=&&_byhl&_i.;

           _POS=MAX(OF _POS, CHKPOS);

        END;

%end;

%end;

%else %do;

%if (%scan(&_forms.,&_i.,~) = $) %then %do;

     FVLS{&_i.}=%scan(&_vars.,&_i.,~);

%end;

%else %do;

%if (&&_mvt&_i. = 1) %then %do;

     IF (&_missv. = 1 & %scan(&_vars.,&_i.,~) = .) THEN FVLS{&_i.}=" ";

%end;

%if (&&_mvt&_i. = 2) %then %do;

     IF (&_missv. = 1 & %scan(&_vars.,&_i.,~) = " ") THEN FVLS{&_i.}=" ";

%end;

     ELSE FVLS{&_i.}=PUT(%scan(&_vars.,&_i.,~), %scan(&_forms.,&_i.,~));

%end;

%end;

%end;

     _CNT=_N_;

RUN;

%if (&_produce. = 1 | &_produce. = 3) %then %do;

PROC PRINT DATA=_XV;VAR _V1-_V&_cntv.;

%if (%length(&_byntv.)) %then %do;BY &_byntv.;PAGEBY &_byntv.;%end;

TITLE "Output from _rectab: SAS Preview of final table";

TITLE2 "Table &_tabname.: &_capt. -- &_tabidnt.";

TITLE3 "NOTE: Column headers are not set up, and probably look wrong";

TITLE4 "To produce final LaTeX table, ensure that _produce=2 or 3";

RUN;

%end;

%if (&_produce. = 2 | &_produce. = 3) %then %do;

PROC TRANSPOSE DATA=_XV OUT=_XXV(RENAME=(A1=_ZX)) PREFIX=A;

     BY &_byntv. _CNT _POS;

%if (&_cntv. > 1) %then %do;

     VAR _V1-_V&_cntv.;

%end;

%else %do;

     VAR _V1;

%end;

RUN;

%let _ccnt=0;

DATA FINAL;

     LENGTH _OP_ $5 JS $1 TX CAPX TABN $200;

     KEEP _OP_ _NM_ _TY_ TX JS NC &_byntv.;

     RETAIN XJUST;

     SET _XXV END=EOF;

%if (%length(&_byntv.)) %then %do;

BY &_byntv.;

IF (FIRST.&_byntv.) THEN DO;

%end;

%else %do;

IF (_N_ = 1) THEN DO;

%end;

        XJUST="&_just.";

        CAPX="&_capt.";TABN="&_tabname.";

%if (%length(&_byntv.)) %then %do;

        BYCX+1;

%if (%length(&_byntfmt.)) %then %do;

        CAPX=TRIM(CAPX)||PUT(&_byntv.,&_byntfmt.);

%end;

%else %do;

        CAPX=COMPRESS(TRIM(CAPX)||&_byntv.);

%end;

        TABN=TRIM(TABN)||COMPRESS(PUT(BYCX,3.));

%end;

        %_tzout(_t=TRIM(CAPX));

        %_tlout(_t=TRIM(TABN));

        %_maout(_t="hdrlns");



        %do _i=1 %to &_cntv.;

           %if (%length(&_strx.)<=0) %then %do;

              %_clout(_j=%substr(&_just.,&_i.,1));

           %end;

           %else %do;

              %* put scan result[%scan(&_strx.,&_i.,~)];

              %if ("%scan(&_strx., &_i.,~)"="_S_") %then %do;

                 %_clout(_j=%substr(&_just.,&_i.,1));

              %end;

              %else %do;

                 %_clout(_j=%substr(&_just.,&_i.,1),

                         _t="%scan(&_strx., &_i.,~)");

              %end;

           %end;

        %end;

*       %_hlout;

        %do _i=1 %to 10;

           zcnt=0;

           %if (%length(&&_hdr&_i.) > 1) %then %do;

              %put _hdr&_i[&&_hdr&_i.];

              %let _j=1;

              %do %while(%length(%scan(&&_hdr&_i.., %eval(&_j), ~)));

                 %let _px=%scan(&&_hdr&_i..,&_j.,~);

                 %let _pa=;%let _pc=;%let _pb=;%let _pd=;

                 %if (%length(&_px.) >= 2) %then %do;

                    %let _pa=%substr(&_px., 1, 1); %end;

                 %if (%length(&_px.) >= 3) %then %do;

                    %let _pb=%substr(&_px., 2, 2); %end;

                 %if (%length(&_px.) >= 6) %then %do;

                    %let _pc=%substr(&_px., 4, 3); %end;

                 %if (%length(&_px.) >= 4) %then %do;

                    %let _pd=%substr(&_px., 4,

                       %eval(%length(&_px.)-3));     %end;

%put   _j[&_j.],_px[&_px.],_pa[&_pa.],_pb[&_pb.],_pc[&_pc.],_pd[&_pd.];

                 %if ("&_pa." = "#") %then %do;

                    %if ("&_pc." ^= "_S_") %then

                       %_txout(_j=c,_t="&_pd.",_n=&_pb.);

                    %else

                        %_skout(_n=&_pb.);

                    zcnt+&_pb.;

                 %end;

                 %else %do;

                    %if ("&_pa." = "!") %then %let _px=&_pd.;

                    zcnt+1;

                    %if ("%scan(&&_hdr&_i.,&_j.,~)"^="_S_") %then %do;

                       justv=substr(xjust, zcnt, 1);

                       %_txout(_j=justv, _jt=0,

                               _t="&_px.",

                               _n=1);

                    %end;

                    %else %do;

                       %_skout(_n=1);

                    %end;

                 %end;

                 %let _j=%eval(&_j.+1);

              %end;

           %end;

        %end;

        %_hlout;

        %_mbout(_t="hdrlns");

        xjust="&_just.";

        zcnt=0;

     end;

     if (_name_ = "_V1" & _POS > 0 & _N_ > 0) THEN

        DO Z=1 TO _POS;%_hlout;END;

     ZCNT+1; JUSTV=SUBSTR(XJUST, ZCNT, 1);



     IF (ZCNT = &_cntv.) THEN ZCNT=0;

     %_txout(_n=1, _t=_zx, _j=justv, _jt=0);

%if (%length(&_byntv.)) %then %do;IF (LAST.&_byntv.) THEN DO;%end;

%else %do;                        IF (EOF    ) THEN DO;       %end;%_hlout;

%if (%length(&_trl1.)) %then %do;

       %_moout(_n=%eval(&_cntv.+1), _t="\strut");

       %_poout(_t="t-&_trwidth.");

%do _i=1 %to 10;

%if (%length(&&_trl&_i.)) %then %do;

%put Checking _pcout: _trl&_i[&&_trl&_i.];

%_pcout(_t="&&_trl&_i.");%end;

%end;

        %_peout;%_meout;%_hlout;

%end;

     END;

RUN;

%_table(_byntc=&_byntc.,

        _byntf=&_byntf.,

        _byntv=&_byntv.,

        _capt=&_capt.,

        _dset=final,

        _extra=&_ext.,

        _filenm=&_filenm.,

        _filext=&_filext.,

        _ladja=&_ladja.,

        _ladjb=&_ladjb.,

        _lineadh=&_lineadh.,

        _locate=&_locate.,

        _longtab=&_longtab.,

        _nc=&_cntv.,

        _sep=&_zepp.,

        _ststrx=&_ststrx.,

        _submdir=&_submdir.,

        _subsdir=&_subsdir.,

        _sw=&_sw.,

        _tabenv=&_tabenv.,

        _tabidnt=&_tabidnt.);

%end;

%mend _rectab;

options nosource;

%macro _table(_byntc=1,

              _byntf=,

              _byntv=,

              _capt=,

              _dset=,

              _extra=0,

              _filenm=,

              _filext=,

              _ladja=,

              _ladjb=,

              _lineadh=,

              _locate=H,

              _longtab=0,

              _nc=,

              _sep=/,

              _ststrx=,

              _submdir=,

              _subsdir=,

              _sw=,

              _tabenv=,

              _tabidnt=);

%let _filenm=%scan(&_filenm,1,.);

PROC SORT DATA=&_dset.;BY &_byntv. _TY_ _NM_;RUN;

%if (&_nc. = 0 | %length(&_nc.) = 0) %then %do;

PROC MEANS DATA=&_DSET. NOPRINT;VAR NC;WHERE (_TY_ = 3 & NC = 1);OUTPUT OUT=REDOX SUM=TOTCOL;RUN;

DATA _NULL_;SET REDOX;CALL SYMPUT("_nc", COMPRESS(PUT(TOTCOL, 6.)));RUN;

%end;

%put ***************************************************************************** *;

%put _nc[&_nc.],_byntc[&_byntc.],_byntf[&_byntf.],_byntv[&_byntv.];

%put ***************************************************************************** *;

%* ****************************************************************** *;

%* _table                                                             *;

%*                                                                    *;

%* Protocol                                                           *;

%* _submdir     - main subdirectory to place final file on            *;

%* _subsdir     - specific subdirectory, under main, for files        *;

%* _filenm      - file name on subdirectory                           *;

%* _filext      - file extension                                      *;

%* _prvars      - variable list to print                              *;

%* _sep         - /                                                   *;

%* _dset        - SAS dataset with data to be set up                  *;

%* _locate      - location code - LaTeX table parameter               *;

%* _nc        - number of columns for table - computed in MEANS       *;

%*                                                                    *;

%* This macro was inspired by F. Poppe, who wrote a file which        *;

%* formatted tables in plain TeX.  This macro formats tables in       *;

%* LaTeX, using various LaTeX constructs.                             *;

%*                                                                    *;

%* The macro reads a dataset &_dset.  This dataset has 4 variables    *;

%* plus optional BY variables, which must be specified in the macro   *;

%* call.                                                              *;

%* 1. NC      - Number of columns                                     *;

%* 2. TX      - Text which goes in the column                         *;

%* 3. _TY_    - Entry type (entry may be indicated with initials      *;

%*    1       - Title (placed in caption)                             *;

%*    2       - Table reference (in label)                            *;

%*    3       - Table definition                                      *;

%*    4       - Normal table text                                     *;

%* 4. _NM_    - number within _TY_                                    *;

%* 5. JS      - Justification of entry.  During _TY_="DEFINE",        *;

%*              default justification is set.  If justification for   *;

%*              for the entry is different later, multicolumn will    *;

%*              be used to set the correct justification              *;

%* 6. _OP_    - Operation                                             *;

%*    e       - End of the line (hard space) - output "\\"            *;

%*    h       - Place a horizontal line (\hline) at this point        *;

%*    c       - Place a horizontal line (\cline) at this point        *;

%*              The value of TX will indicate the columns             *;

%*    s       - Skip a column or columns, defined in the NCOL spec    *;

%*    mo      - multicolumn open - starts a multicolumn specification *;

%*    me      - multicolumn end                                       *;

%*    po      - parbox open - enter location and size in TX           *;

%*              as "t-5in" (top-5 inch)                               *;

%*    pc      - parbox continue                                       *;

%*    pe      - parbox end - TX is placed on file - "}" entered too   *;

%*    ma      - macro start                                           *;

%*    mb      - macro end                                             *;

%*                                                                    *;

%*                                                                    *;

%*                                                                    *;

%* Basic approach                                                     *;

%* _TY_ = 1 - enter caption                                           *;

%* _TY_ = 2 - set up label                                            *;

%*                                                                    *;

%* _TY_ = 3                                                           *;

%* In this section, the basic table is defined.  Each item defines    *;

%* the specification for one column.  If NCOL = 1, the item defines   *;

%* a column.  If NCOL = 0, the item defines either a vertical line    *;

%* at the right of the previous column (if TX = "|") or an            *;

%* @-expression (TX is placed between "@{" and "}").                  *;

%*                                                                    *;

%* _TY_=4                                                             *;

%* In this section, the items are placed into columns in the order    *;

%* in which they are received.  The NCOL specification determines the *;

%* number of columns which will be used -if NCOL > 1, a multicolumn   *;

%* environment will be used to frame the tx.  At the end of each      *;

%* item except the item at the end of the line, an "&" will be        *;

%* written to the file.  The items will be counted, and a "\\" will   *;

%* be written to the file when the number of items in the table is    *;

%* encountered, or _TY_="END" is found.                               *;

%*                                                                    *;

%* Error conditions                                                   *;

%* o If an incorrect number of items is output, a NOTE will be output *;

%* o If a multicolumn item spans the end of a line, a NOTE will be    *;

%*   output                                                           *;

%* o                                                                  *;

%*                                                                    *;

%*                                                                    *;

%* ****************************************************************** *;

%let _fx=;

%if (%length(&_submdir.)) %then %let _fx=&_submdir.&_sep.;

%if (%length(&_subsdir.)) %then %let _fx=&_fx.&_subsdir.&_sep.;

%if (&_byntc. = 0) %then %let _byntc=1;

%do _i=1 %to &_byntc.;

%if (&_byntf.) %then %do;

FILENAME XF&_i. "&_fx.&_filenm.&_i..&_filext.";

%end;

%else %do;

FILENAME XF&_i. "&_fx.&_filenm..&_filext.";

%end;

%end;

DATA _RNULLX ;LENGTH PTC PTL $200;RETAIN PTC PTL;

     SET &_dset. END=EOF;

%if (%length(&_byntv.)) %then %do;BY &_byntv.;%end;;

     TNCOL=&_nc.;

     RETAIN STRTFLG SETFLG EXCHK 1 MULTIOP PAROP FCNT TZ 0;

%if (%length(&_byntv.)) %then %do;

     IF (FIRST.&_byntv.) THEN DO;

%end;

%else %do;

     IF (_N_ = 1) THEN DO;

%end;

        FCNT+1;

%do _i=1 %to &_byntc.;

        IF (FCNT = &_i.) THEN FILE XF&_i.;

%end;

        STRTFLG=1; SETFLG=1;

%if (%length(&_byntv.)) %then %do;

        PUT "%% &_tabidnt." &_byntv. &_byntfmt.;

%end;

%else %do;

        PUT "%% &_tabidnt.";

%end;

%if (%length(&_tabenv.)) %then %do;

        PUT "{&_tabenv.";

%end;

%if (&_longtab.) %then %do;

        PUT "\begin{&_sw.table}[c]{&_ststrx." @;

%end;

%else %do;

%if ("&_locate." ^= " ") %then PUT "\begin{&_sw.table}[&_locate.]";

%else                          PUT "\begin{&_sw.table}";;

%end;

     END;

%do _i=1 %to &_byntc.;

     IF (FCNT = &_i.) THEN FILE XF&_i.;

%end;

     IF ((_TY_ = 1) & ("&_capt" ^= "_NULL_")) THEN

        PTC="\caption{"||TRIM(TX)||"}";

     IF (_TY_ = 2) THEN

        PTL="\label{"||TRIM(TX)||"}";

%if (^&_longtab.) %then %do;

     IF ((_TY_ = 1) & ("&_capt" ^= "_NULL_")) THEN PUT PTC;

     IF  (_TY_ = 2) THEN PUT PTL;

     IF (STRTFLG & _TY_ >= 2) THEN DO;

        STRTFLG=0;

        PUT "\begin{center}";

        PUT "\begin{tabular}{&_ststrx." @;

        TI=0;

     END;

%end;



     ARRAY COLJUST {&_nc.} $ 1 _TEMPORARY_;

     ARRAY COLBAR  {&_nc.}     _TEMPORARY_;



     IF (_TY_ = 3) THEN DO;

%* ****************************************************************** *;

%* This is the define section - check conditions for define here      *;

%* after the define section is finished                               *;

%*                                                                    *;

%* If nc = 1, standard column                                         *;

%* If nc = 0, define some special column specifications               *;

%* Otherwise an error is raised                                       *;

%*                                                                    *;

%* coljust - justification for column                                 *;

%* colbar  - place bar after this column                              *;

%* ****************************************************************** *;

        IF (NC = 1) THEN DO;

           PUT JS +(-1) @;

           TI+1;

           IF (TX ^= " " & LENGTH(TX) > 0) THEN DO;

              IF (TX ^= "|") THEN DO;

                 TX="@{"||TRIM(TX)||"}";

                 COLBAR{TI}=0;

              END;

              ELSE DO;

                 TX="|";

                 COLBAR{TI}=1;

              END;

           END;

           COLJUST{TI}=JS;

        END;

        ELSE IF (NC = 0) THEN DO;

           IF (TX = "|") THEN DO;

              IF (TI > 0) THEN

                 COLBAR{TI}=1;

           END;

           ELSE TX="@{"||TRIM(TX)||"}";

        END;

        ELSE ERROR "nc > 1 during define - invalid";

        IF (TX ^= "") THEN PUT TX +(-1) @;

        IF (TI = &_nc. & &_extra.) THEN PUT "l" @;

     END;

     ELSE IF (_TY_ = 4) THEN DO;

%* ****************************************************************** *;

%* This is the table body section                                    *;

%* First check to see if this is the first item - finish tabular     *;

%* ****************************************************************** *;

        IF (SETFLG) THEN DO;

           PUT "}";

%if (&_longtab.) %then %do;

           PUT PTC / PTL;

%end;

           SETFLG=0;

           TI=0;

        END;

%* ****************************************************************** *;

%* Now increment ti - counter for columns                            *;

%* ****************************************************************** *;

        IF (_OP_ NOT IN ("h","pe","pc","me","ma","mb")) THEN TI+NC;

        IF (_OP_ = "c") THEN DO;

%* ****************************************************************** *;

%* Here set up a cline specification                                 *;

%* ****************************************************************** *;

           PTX="\cline{"||trim(tx)||"}";

           PUT PTX;

           OSEC=SCAN(TX, 1, "-");

           TIX=INPUT(OSEC,5.);

           IF (TI > TIX) THEN ERROR "Current column is beyond start of cline";

           OSEC=SCAN(TX, 2, "-");

           TI=INPUT(OSEC,5.);

           IF (TI > &_nc.) THEN ERROR "Invalid final column for cline";

        END;

        ELSE IF (_OP_ = "e") THEN DO;

%* ****************************************************************** *;

%* Here end of line specification                                     *;

%* ****************************************************************** *;

           IF (EXCHK) THEN PUT " &_ladja. \\ &_ladjb. "; TI=0;

           EXCHK=1;

        END;

        ELSE IF (_OP_ = "ma") THEN DO;

%* ****************************************************************** *;

%* Here macro specification                                           *;

%* ****************************************************************** *;

           PUT "\global\def\" TX +(-1) "{";TZ=1;EXCHK=0;

        END;

        ELSE IF (_OP_ = "mb") THEN DO;

%* ****************************************************************** *;

%* Here macro specification                                           *;

%* ****************************************************************** *;

           PUT "}";

           IF (&_longtab.) THEN DO;

              PUT "\" TX /

                 "\endfirsthead" /

                 "\multicolumn{&_nc.}{r}{\small\slshape "

                     "continued from previous page}" /

                 "\" TX /

                 "\endhead" / "\hline"

                 "\multicolumn{&_nc.}{r}{\small\slshape "

                     "continued on next page} \\ \hline" /

                 "\endfoot" /

                 "\endlastfoot";

           END;

           ELSE DO;

              PUT "\" TX;

           END;

           EXCHK=0;

        END;

        ELSE IF (_OP_ = "h") THEN DO;

%* ****************************************************************** *;

%* Here set up a hline specification                                 *;

%* ****************************************************************** *;

           IF (TZ = 0) THEN DO;

              IF (TI <= 1) THEN DO;

                 PUT "  &_lineadh. \hline"; TI=0;

              END;

              ELSE ERROR "Issuing \hline specification in middle of line";

           END;

           TZ=0;

        END;

        ELSE IF (_OP_ = "s") THEN DO;

%* ****************************************************************** *;

%* Here set up a column skip specification                           *;

%* ****************************************************************** *;

           ptx="\multicolumn{"||put(nc,2.)||"}{c}{}";

           put ptx @;

        end;

        else if (_op_ = "pc") then do;

           put tx;

        end;

        else if (_op_ = "pe" | _op_ = "me") then do;

           put tx "}";

           if (_op_ = "me") then do;

              if (multiop) then multiop=0;

              else ERROR "Multiline multicolumn close issued when not open";

           end;

           if (_op_ = "pe") then do;

              if (parop) then parop=0;

              else ERROR "Multiline parbox close issued when not open";

           end;

        end;

        else if (_op_ = "mo") then do;

/*         ptx="\multicolumn{"||put(nc,2.)||"}{"||

*             trim(js)||"}{"||trim(tx);              */

           ptx="\multicolumn{"||put(nc,2.)||"}{l"||

              "}{"||trim(tx);

           put ptx;

           if (multiop) then

              ERROR "Opening second multiline multicolumn specifications";

           multiop=1;

        end;

        else if (_op_ = "po") then do;

           locat=scan(tx, 1, "-");

           if (length(locat) > 0) then

              ptx="\parbox["||trim(locat)||"]";

           else ptx="";

           ptx=trim(ptx)||"{"||trim(scan(tx,2,"-"))||"}{";

           put ptx;

           if (parop) then

              ERROR "Opening second multiline parbox specifications";

           PAROP=1;

        END;

        ELSE DO;

           IF (NC > 1 |

               (COLJUST{TI} ^= JS & JS ^= "")) THEN DO;



              IF (COLBAR{TI}) THEN

                 PTX="\multicolumn{"||PUT(NC,2.)||"}{"||

                    TRIM(JS)||"|"||"}{"||TRIM(TX)||"}";

              ELSE

                 PTX="\multicolumn{"||PUT(NC,2.)||"}{"||

                    TRIM(JS)||"}{"||TRIM(TX)||"}";

              PUT PTX @;

           END;

           ELSE PUT TX @;

        END;

        IF (MULTIOP | PAROP) THEN;

        ELSE IF (0 < TI < &_nc.) THEN PUT "&";

        ELSE DO;

           IF (_OP_ ^= "h") THEN DO;

              IF (EXCHK) THEN PUT " &_ladja. \\ &_ladjb. ";EXCHK=1; TI=0;

           END;

        END;

     END;

%if (%length(&_byntv.)) %then %do;IF (LAST.&_byntv.) THEN DO;%end;

%else %do;                        IF (EOF   ) THEN DO;       %end;

%if (^&_longtab.) %then PUT "\end{tabular}" / "\end{center}";;

        PUT "\end{&_sw.table}";

%if (%length(&_tabenv.)) %then PUT "}";;

     END;

run;

%mend _table;

%macro _maktab;

%mend  _maktab;

%macro _hlout;

%* ****************************************************************** *;

%* This specification creates an observation which outputs a line    *;

%* ****************************************************************** *;

     do; nc=1; _nm_+1; js=""; _op_="h"; _ty_=4; tx=""; output; end;

%mend _hlout;

%macro _skout(_n=1);

%* ****************************************************************** *;

%* This specification creates an observation which skips _nc cols  *;

%* ****************************************************************** *;

     do; nc=&_n.; _nm_+1; js=""; _op_="s"; tx=""; _ty_=4; output; end;

%mend _skout;

%macro _txout(_n=1, _t=" ", _j=l, _jt=1);

%* ****************************************************************** *;

%* This specification creates an obs with the VALUE of _t            *;

%* That is, whatever _t is is just put into the variable TX        *;

%* ****************************************************************** *;

do; nc=&_n.; _ty_=4; _nm_+1;

%if (&_jt. = 1) %then %do;js="&_j."; %end;

%else %do; js=&_j.; %end; _op_=""; tx=&_t.; output; end;

%mend _txout;

%macro _tlout(_n=1, _t=" ", _j=l);

%* ****************************************************************** *;

%* This specification creates an label specification                 *;

%* That is, whatever _t is is just put into the variable TX        *;

%* ****************************************************************** *;

do; nc=&_n.; _ty_=2; _nm_+1; js="&_j."; _op_=""; tx=&_t.; output; end;

%mend _tlout;

%macro _tzout(_n=1, _t=" ", _j=l);

%* ****************************************************************** *;

%* This specification creates an title specification                 *;

%* That is, whatever _t is is just put into the variable TX        *;

%* ****************************************************************** *;

do; nc=&_n.; _ty_=1; _nm_+1; js="&_j."; _op_=""; tx=&_t.; output; end;

%mend _tzout;

%macro _clout(_j=l, _t=" ");

%* ****************************************************************** *;

%* This specification creates an obs which sets up a column, and     *;

%* which converts _t into tx in terms of VALUE, not MEANING        *;

%* ****************************************************************** *;

     do; nc=1; _nm_+1; js="&_j."; _op_=""; _ty_=3; tx=&_t.; output; end;

%mend _clout;

%macro _moout(_t=" ", _n=1);

%* ****************************************************************** *;

%* This specification creates an obs which sets up a column, and     *;

%* which converts _t into tx in terms of MEANING not VALUE         *;

%* ****************************************************************** *;

     do; nc=&_n.; _nm_+1; js=""; _op_="mo"; _ty_=4; tx=&_t.; output; end;

%mend _moout;

%macro _pcout(_t=" ");

%* ****************************************************************** *;

%* This specification creates an obs which sets up a column, and     *;

%* which converts _t into tx in terms of MEANING not VALUE         *;

%* ****************************************************************** *;

     do; nc=1; _nm_+1; js=""; _op_="pc"; _ty_=4; tx=&_t.; output; end;

%mend _pcout;

%macro _poout(_t=" ");

%* ****************************************************************** *;

%* This specification creates an obs which sets up a column, and     *;

%* which converts _t into tx in terms of MEANING not VALUE         *;

%* ****************************************************************** *;

     do; nc=1; _nm_+1; js=""; _op_="po"; tx=&_t.; _ty_=4; output; end;

%mend _poout;

%macro _meout(_t=" ");

%* ****************************************************************** *;

%* This specification creates an obs which sets up a column, and     *;

%* which converts _t into tx in terms of MEANING not VALUE         *;

%* ****************************************************************** *;

     do; nc=1; _nm_+1; js=""; _op_="me"; tx=&_t.; _ty_=4; output; end;

%mend _meout;

%macro _peout(_t=" ");

%* ****************************************************************** *;

%* This specification creates an obs which sets up a column, and     *;

%* which converts _t into tx in terms of MEANING not VALUE         *;

%* ****************************************************************** *;

     do; nc=1; _nm_+1; js=""; _op_="pe"; tx=&_t.; _ty_=4; output; end;

%mend _peout;

%macro _maout(_t=" ");

%* ****************************************************************** *;

%* This specification creates an obs which sets up a column, and     *;

%* which converts _t into tx in terms of MEANING not VALUE         *;

%* ****************************************************************** *;

     do; nc=1; _nm_+1; js=""; _op_="ma"; tx=&_t.; _ty_=4; output; end;

%mend _maout;

%macro _mbout(_t=" ");

%* ****************************************************************** *;

%* This specification creates an obs which sets up a column, and     *;

%* which converts _t into tx in terms of MEANING not VALUE         *;

%* ****************************************************************** *;

     do; nc=1; _nm_+1; js=""; _op_="mb"; tx=&_t.; _ty_=4; output; end;

%mend _mbout;

options source;

***** SAS Macro  *****;

*
;