*
; /* 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 *****; *;