/*

/ Program   : aetab.sas
/ Version   : 2.4
/ Author    : Roland Rashleigh-Berry
/ Date      : 30-Dec-2011
/ Purpose   : To create a multi-level AE table of counts and percentages
/ SubMacros : %freqlvls %trnslvls %comblvls %prntlvls %mvarlist %removew
/             %hasvarsc %varnum %nlobs %varlist %mvarvalues %quotelst %words
/             %npctpvals %zerogrid (assumes %popfmt already run)
/ Notes     : You must have run %popfmt before calling this macro and the 
/             treatment variable and the patient unique identifier you supplied
/             to %popfmt must be present in the input AE dataset.
/
/             The output must remain as pure ascii output because of the use of
/             split characters which will not be rendered correctly in ODS.
/
/             You only need specify the first two positional parameters to get
/             a report. The column labels will be generated from the variable
/             labels in that case.
/
/             The default ordering of the output is by descending patient
/             frequency count for the totals treatment arm. To override this
/             ordering you have to specify numeric informats to the parameters
/             LVL1INFMT etc.. The %mkordinfmt macro might be useful to create
/             these numeric informats.
/
/             You are not limited to ten footnotes if you use the pageline or
/             endline parameters. If you have a footnotes macro that works with
/             compute blocks in proc report then you can specify the macro name
/             to the pagemacro= or endmacro= parameters.
/
/             Note that this macro uses the %splitvar macro to align characters
/             and this only works for Western character sets. See the %splitvar
/             macro header for more information.
/
/ Usage     : %aetab(ae3,msoc mhlgt mhlt mpt aeint)
/
/             %aetab(dsin=ae3,varlist=msoc mhlgt mhlt mpt aeint)
/
/             %aetab(dsin=ae3,varlist=msoc mhlgt mhlt mpt aeint,trtalign=center,
/                  colw=48,trtlabel="_Treatment Arms_" " ",total=yes,events=yes,
/                  alllowlvl=yes,alllowwhere=lvl5 ne ".",lowinfmt=int.);
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ dsin              (pos) Input dataset
/ msglevel=X        Message level to use inside this macro for notes and 
/                   information written to the log. By default, both Notes and
/                   Information are suppressed. Use msglevel=N or I for more
/                   information.
/ varlist           (pos) AE levels variable list (current maximum of 9)
/ total=yes         By default, show the column for the totals of all treatments
/ trtfmt            (optional) Format to override the default format created by
/                   %popfmt.
/ events=no         By default, do not show event counts
/ print=yes         By default, print the output. Set this to no to make the
/                   internal datasets available for further processing.
/ pctfmt=5.1        Format for the percentage
/ pctsign=no        By default, do not show the % sign for the percentage
/ minpct            Minimum percentage below which items will be dropped
/                   (applies to the trtord treatment arm)
/ trtord            Treatment value for ordering the output and the arm for the
/                   minpct value to be applied to (defaults to the total
/                   treatment arm value).
/ pvalues=no        By default, do not calculate p-values
/ pvaltrtlist       List of treatment arm values (separated by spaces) used for
/                   p-value calculation (defaults to not using the value
/                   assigned to _trttotstr_ ).
/ pvalvar=_pvalue   Name of numeric p-value variable
/ pvalstr=_pvalstr  Name of character p-value variable
/ pvallbl="p-value" Label for numeric and character p-value variables (quoted)
/ pvalfmt=p63val.   Default format (created inside this macro) for formatting
/                   p-value statistic (6.3 unless <0.001 or >0.999).
/ pvalkeep=         Expression for p-value values to keep. If condition is not
/                   met then numeric and string values are set to missing.
/ fisherid=^        Symbol to suffix formatted p-values for the Fisher exact
/                   test.
/ chisqid=~         Symbol to suffix formatted p-values for the Chi-square test
/ nfmt=3.           Format for the N count (will be corrected if too small)
/ evfmt=4.          Format for the event count (will be corrected if too small)
/ colw=40           Column width for the combined AE terms
/ split=@           Split character for proc report
/ usecolon=yes      This is a splitting control and tells the %splitvar macro
/                   that you want text that flows onto further lines to align
/                   with any colon present if near the start of the string.
/ spacing=4         Spacing between the columns
/ topline=yes       Default is to show a line at the top of the report
/ indent=3          Indentation for each AE level
/ hindent=0         Hanging indent for lines that flow onto following lines
/ trtalign=center   Column label alignment
/ trtlabel          Label to show over the treatment arm columns
/ comblabel         Label for the AE terms column. If not specified then this
/                   label will be constructed from the variable labels.
/ breaklvl=1        Level at which you want to skip lines in proc report
/ alllowlvl=no      By default, do not show all the possible low level terms
/                   for every higher level term.
/ alllowwhere       Optional where term to limit the number of low level terms
/                   when using the alllowlvl=yes option.
/ lvl1anylbl="Patients with any Adverse Event"  Label to use for the first line
/                   of the report which is a count of unique patients with AEs.
/                   If you set this to " " then this line will be dropped.
/ lowinfmt          The lowest level numeric ordering informat (must be
/                   specified if alllowlvl=yes set).
/ lvl1-9infmt       Names of numeric informats to change the default ordering of
/                   the AE table.
/ pageline=no       By default, do not show a line at the bottom of the page.
/ pageline1-9       Footnote lines to show at the end of the page
/ pagemacro         Name of the footnotes macro (no % sign) that creates
/                   footnotes in proc report compute blocks.
/ endline=no        By default, do not show a line at the end of the report
/ endline1-9        Footnote lines to show at the end of the report
/ endmacro          Name of the footnotes macro (no % sign) that creates
/                   footnotes in proc report compute blocks.
/ filtercode        SAS code you specify to drop observations and do minor
/                   reformatting before printing is done. If this code
/                   contains commas then enclose in quotes (the quotes will be
/                   dropped from the start and end before the code is executed).
/                   You can have multiple lines of sas code if you end each line
/                   with a semicolon. For serious editing you should do this in
/                   a macro defined to extmacro= .
/ extmacro          External macro to call (no % sign) and will typically be
/                   used to include or drop stats values in the report.
/ dsparam           Name of parameter dataset. This can EITHER be a "flat"
/                   dataset with variable names matching parameter names OR a
/                   Name-Value pair "tall" dataset (both Name and Value must be
/                   character variables and be called "Name" and "Value" in the
/                   input dataset) with the contents of Name matching a parameter
/                   name and Value its value. "Tall" datasets are suited to the
/                   metadata-driven use of this macro. In both cases, variables
/                   should be character variables. Numeric values can be used
/                   but they must be supplied as characters. Note that parameter
/                   values that are normally supplied in quotes such as 'Courier'
/                   must be enclosed in extra quotes such as Value="'Courier'"
/                   when building the parameter dataset.
/
/                   You can use dataset modifiers when specifying the input
/                   dataset and these modifiers will be applied to create the
/                   internal work dataset "_dsparam". Do not call your parameter
/                   dataset "_dsparam" as this is reserved for use inside this
/                   macro and will be deleted.
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ rrb  28Oct11         New (v1.0)
/ rrb  31Oct11         Parameter dataset and pctsign= handling added plus nfmt=
/                      and evfmt= values will be corrected if too small (v1.1)
/ rrb  31Oct11         minpct= processing for total column added (v1.2)
/ rrb  02Nov11         pvalue processing added (v2.0)
/ rrb  03Nov11         trtord= processing added (v2.1)
/ rrb  08Nov11         filtercode= and extmacro= processing added (v2.2)
/ rrb  14Nov11         Major bug fixed for p-values calculations. Changed the
/                      dataset pre-processing for calling the p-values macro to
/                      add zero observations where there is no match with the
/                      "totals" treatment arm (v2.3)
/ rrb  26Dec11         Header updated to explain that this macro only works
/                      correctly for Western character sets.
/ rrb  30Dec11         msglevel= processing added (v2.4)
/===============================================================================
/ Copyright (C) 2011, Roland Rashleigh-Berry. Use of this software is by license
/ only commencing 01 Jan 2012 although permission is granted to use these macros
/ for educational or demonstration purposes and by drug regulatory agencies.
/
/ Users should ensure this software is suitable for the purpose to which it is 
/ put and to provide adequate checks on the accuracy of any values produced as
/ no guarantee can be given that the results displayed by this software are as
/ expected and no liability is accepted for any damage caused through use of any
/ incorrect output produced. Only use this software if you agree to these terms.
/=============================================================================*/

%put MACRO CALLED: aetab v2.4;

%macro aetab(dsin,
          varlist,
         msglevel=X,
            total=yes,
           trtfmt=,
           events=no,
            print=yes,
             nfmt=3.,
           pctfmt=5.1,
          pctsign=no,
           minpct=,
           trtord=,
          pvalues=no,
      pvaltrtlist=,
          pvalvar=_pvalue,
          pvalstr=_pvalstr,
          pvallbl="p-value",
          pvalfmt=p63val.,
         pvalkeep=,
         fisherid=^,
          chisqid=~,
            evfmt=4.,
             colw=40,
            split=@,
         usecolon=yes,
          spacing=4,
          topline=yes,
           indent=3,
          hindent=0,
         trtalign=center,
         trtlabel=,
        comblabel=,
         breaklvl=1,
        alllowlvl=no,
      alllowwhere=,
       lvl1anylbl="Patients with any Adverse Event",
         lowinfmt=,
        lvl1infmt=,
        lvl2infmt=,
        lvl3infmt=,
        lvl4infmt=,
        lvl5infmt=,
        lvl6infmt=,
        lvl7infmt=,
        lvl8infmt=,
        lvl9infmt=,
         pageline=no,
        pageline1=,
        pageline2=,
        pageline3=,
        pageline4=,
        pageline5=,
        pageline6=,
        pageline7=,
        pageline8=,
        pageline9=,
       pageline10=,
       pageline11=,
       pageline12=,
       pageline13=,
       pageline14=,
       pageline15=,
        pagemacro=,
          endline=no,
         endline1=,
         endline2=,
         endline3=,
         endline4=,
         endline5=,
         endline6=,
         endline7=,
         endline8=,
         endline9=,
        endline10=,
        endline11=,
        endline12=,
        endline13=,
        endline14=,
        endline15=,
         endmacro=,
       filtercode=,
         extmacro=,
          dsparam=
            );

  %local parmlist;

  %*- get a list of parameters for this macro -;
  %let parmlist=%mvarlist(aetab);

  
  %local savopts lvls lvllist plugwith trtw trtvar trttot trtpref trtvars 
         uniqueid maxn maxev err errflag pvalds pvalpr;

  %let err=ERR%str(OR);
  %let errflag=0;

  %if not %length(&msglevel) %then %let msglevel=X;
  %let msglevel=%upcase(%substr(&msglevel,1,1));
  %if "&msglevel" NE "N" and "&msglevel" NE "I" %then %let msglevel=X;

  %let savopts=%sysfunc(getoption(msglevel,keyword)) %sysfunc(getoption(notes,keyword));
  %if "&msglevel" EQ "N" or "&msglevel" EQ "I" %then %do;
    options msglevel=&msglevel;
  %end;
  %else %do;
    options nonotes;
  %end;

             /*-----------------------------------------*
                      Parameter dataset handling
              *-----------------------------------------*/


  %if %length(&dsparam) %then %do;



    %*- remove the macro variable name "parmlist" from this list -;
    %let parmlist=%removew(&parmlist,parmlist);

    *-- handle possible dataset modifiers --;
    data _dsparam;
      set &dsparam;
    run;
 
    %if %hasvarsc(_dsparam,name value) %then %do;
      *-- we have a Name-Value pair dataset so transpose it to a "flat" dataset --;
      proc transpose data=_dsparam(keep=name value) out=_dsparam(drop=_name_);
        var value;
        id name;
      run;
 
      %if %varnum(_dsparam,_label_) %then %do;
        *-- drop the _label_ --;
        data _dsparam,
          set _dsparam:
          drop _label_;
        run;
      %end;
    %end;

    %if %nlobs(_dsparam) NE 1 %then %do;
      %let errflag=1;
      %put &err: (aetab) The parameter dataset dsparam=&dsparam should have one;
      %put &err: (aetab) observation but this dataset has %nlobs(_dsparam) observations.;
      %put &err: (aetab) Checking of this dataset will continue but it can not be used.;
      %put;
    %end;

    %let varlist2=%varlistn(_dsparam);
    %if %length(&varlist2) %then %do;
      %let errflag=1;
      %put &err: (aetab) Numeric variables are not allowed in the parameter dataset ;
      %put &err: (aetab) dsparam=&dsparam but the following numeric variables exist:;
      %put &err: (aetab) &varlist2;
      %put;
    %end;

    %if %varnum(_dsparam,dsparam) %then %do;
      %let errflag=1;
      %put &err: (aetab) The variable DSPARAM is present in the parameter dataset;
      %put &err: (aetab) dsparam=&dsparam but use of this variable inside a;
      %put &err: (aetab) parameter dataset is not allowed.;
      %put;
    %end;

    proc contents noprint data=_dsparam out=_unicont(keep=name);
    run;

    data _null_;
      length badvars $ 2000;
      retain badvars ;
      set _unicont end=last;
      name=upcase(name);
      if name not in (%quotelst(&parmlist)) then badvars=trim(badvars)||" "||name;
      if last then call symput('badvars',trim(left(badvars)));
    run;

    %if %length(&badvars) %then %do;
      %let errflag=1;
      %put &err: (aetab) The following list of variables in dsparam=&dsparam;
      %put &err: (aetab) do not match any of the macro parameter names so the;
      %put &err: (aetab) parameter dataset will not be used:;
      %put &err: (aetab) &badvars;
      %put;
    %end;

    proc datasets nolist;
      delete _unicont;
    run;
    quit;

    %if &errflag %then %goto exit;

    *- the parameter dataset is good so call symput all the variables -;
    data _null_;
      set _dsparam;
      array _char {*} _character_;
      length __y $ 32;
      do __i=1 to dim(_char);
        __y=vname(_char(__i));
        call symput(__y,trim(left(_char(__i))));
      end;
    run;

    %let varlist2=%varlist(_dsparam);

    proc datasets nolist;
      delete _dsparam;
    run;
    quit;

    %put MSG: (aetab) The following macro parameters and their values were;
    %put MSG: (aetab) set as the result of use of the dsparam=&dsparam;
    %put MSG: (aetab) parameter dataset:;
    %mvarvalues(&varlist2);
    %put;

  %end;


      /*-----------------------------*
           check parameter settings
       *-----------------------------*/ 


  %let trtvar=&_trtvar_;

  %if not %length(&trtord) %then %let trtord=&_trttotstr_;
  %let trttot=&_trttotstr_;
  %let trtpref=&_trtpref_;
  %let uniqueid=&_uniqueid_;

  %if not %length(&trtfmt) %then %let trtfmt=&_popfmt_;

  %if not %length(&pvalues) %then %let pvalues=no;
  %let pvalues=%upcase(%substr(&pvalues,1,1));

  %if not %length(&total) %then %let total=yes;
  %let total=%upcase(%substr(&total,1,1));

  %if not %length(&pctsign) %then %let pctsign=no;
  %let pctsign=%upcase(%substr(&pctsign,1,1));

  %let trtvars=&_trtvarlist_;
  %if &total EQ Y %then %let trtvars=&_trtvarlist_ &_trttotvar_;


  %if not %length(&print) %then %let print=yes;
  %let print=%upcase(%substr(&print,1,1));

  %if not %length(&events) %then %let events=no;
  %let events=%upcase(%substr(&events,1,1));

  %if not %length(&pvaltrtlist) %then %let pvaltrtlist=ne &_trttotstr_;
  %else %let pvaltrtlist=in (&pvaltrtlist);

  %let lvls=%words(&varlist);

  %let lvllist=;
  %do i=1 %to &lvls;
    %let lvllist=&lvllist lvl&i;
  %end;

  %if not %length(&indent) %then %let indent=3;

  %if not %length(&comblabel) %then %do;
    %let comblabel="%varlabel(%scan(&dsin,1,%str(%()),%scan(&varlist,1,%str( )))";
    %do i=2 %to &lvls;
      %let comblabel=&comblabel
      "%sysfunc(repeat(%str( ),%eval((&i-1)*&indent-1)))%varlabel(%scan(&dsin,1,%str(%()),%scan(&varlist,&i,%str( )))";
    %end;
  %end;


      /*----------------------------------------------*
          calculate frequency counts and percentages 
       *----------------------------------------------*/ 


  %freqlvls(dsin=&dsin,varlist=&varlist,trtvar=&trtvar,trttot=&trttot,
            mvarmax=maxn,nodupvars=&uniqueid,dsout=_patcnt);

  %let maxn=&maxn;
  %if %length(&maxn) GT %scan(&nfmt,1,.) %then %let nfmt=%length(&maxn).;

  %if &events EQ Y %then %do;

    %freqlvls(dsin=&dsin,varlist=&varlist,trtvar=&trtvar,trttot=&trttot,calcord=no,
              mvarmax=maxev,dsout=_events);

    %let maxev=&maxev;
    %if %length(&maxev) GT %scan(&evfmt,1,.) %then %let evfmt=%length(&maxev).;

    data _tranrdy;
      merge _patcnt _events(rename=(_freq_=_events));
      by &trtvar &lvllist;
    run;

    data _tranrdy;
      %if &pctsign EQ Y %then %do;
        retain pctplug "%) ";
      %end;
      %else %do;
        retain pctplug ") ";
      %end;
      length str $ 30;
      merge _popfmt _tranrdy end=last;
      by &trtvar;
      _pct=100*_freq_/_total;
      str=put(_freq_,&nfmt)||" ("||put(_pct,&pctfmt)||pctplug||put(_events,&evfmt);
      if last then do;
        call symput('plugwith','"'||put(0,&nfmt)||" ("||put(0,&pctfmt)||pctplug||put(0,&evfmt)||'"');
      end;
      drop pctplug;
    run;

  %end;

  %else %do;

    data _tranrdy;
      %if &pctsign EQ Y %then %do;
        retain pctplug "%)";
      %end;
      %else %do;
        retain pctplug ")";
      %end;
      length str $ 30;
      merge _popfmt _patcnt end=last;
      by &trtvar;
      _pct=100*_freq_/_total;
      str=put(_freq_,&nfmt)||" ("||put(_pct,&pctfmt)||pctplug;
      if last then do;
        call symput('plugwith','"'||put(0,&nfmt)||" ("||put(0,&pctfmt)||pctplug||'"');
      end;
      drop pctplug;
    run;

  %end;


  %if &pvalues EQ Y %then %do;
   
    *- make sure we have zero _freq_ values for all combinations -;
    %zerogrid(zerovar=_freq_,
              dsout=_zerogrid(where=(&trtvar &pvaltrtlist)),
              var1=&trtvar _total,ds1=_popfmt,
              var2=&lvllist,ds2=_tranrdy);

    *- sort the data ready to merge on top of zero values -;
    proc sort data=_tranrdy(keep=&trtvar &lvllist _freq_
                           where=(&trtvar &pvaltrtlist))
               out=_forpvals;
      by &trtvar &lvllist;
    run;

    *- merge on top of zero _freq_ values -;
    data _forpvals;
      merge _zerogrid _forpvals;
      by &trtvar &lvllist;
    run;

    *- generate the value for the p-value calculation -;
    data _forpvals;
      set _forpvals;
      _response=1;
      _weight=_freq_;
      output;
      _response=0;
      _weight=_total-_freq_;
      output;
      keep &lvllist &trtvar _response _weight;
    run;

    proc sort data=_forpvals;
      by &lvllist &trtvar;
    run;

    %if %attrn(_forpvals,nobs) GT 0 %then %do;
      %let pvalds=_pvalues;
      %let pvalpr=&pvalstr;
      %npctpvals(dsin=_forpvals,byvars=&lvllist,pvalvar=&pvalvar,pvallbl=&pvallbl,
                 pvalstr=&pvalstr,pvalfmt=&pvalfmt,pvalkeep=&pvalkeep,
                 trtvar=&trtvar,respvar=_response,countvar=_weight,
                 chisqid=&chisqid,fisherid=&fisherid);

      %if %length(&lvl1anylbl) %then %do;
        data _pvalues;
          set _pvalues;
          if lvl1=:"ANY " then lvl1=&lvl1anylbl;
          if lvl1=" " then delete;
        run;
        proc sort data=_pvalues;
          by &lvllist;
        run;
      %end;

    %end;

    proc datasets nolist;
      delete _zerogrid _forpvals;
    run;
    quit;

  %end;


      /*----------------------------------------------*
            transpose the values by treatment arm 
       *----------------------------------------------*/ 

  %trnslvls(dsin=_tranrdy,dsout=_tran,var=str,trtvar=&trtvar,trtfmt=&trtfmt,
  trtord=&trtord,alllowlvl=&alllowlvl,alllowwhere=&alllowwhere,lowinfmt=&lowinfmt,
  plugwith=&plugwith,lvls=&lvls,lvl1anylbl=&lvl1anylbl,prefix=&trtpref,
  lvl1infmt=&lvl1infmt,lvl2infmt=&lvl2infmt,lvl3infmt=&lvl3infmt,
  lvl4infmt=&lvl4infmt,lvl5infmt=&lvl5infmt,lvl6infmt=&lvl6infmt,
  lvl7infmt=&lvl7infmt,lvl8infmt=&lvl8infmt,lvl9infmt=&lvl9infmt);

  %if %length(&minpct) %then %do;
    proc sort data=_tranrdy out=_tranpct;
      by &lvllist &trtvar;
    run;
    proc transpose prefix=_PCT data=_tranpct out=_tranpct(drop=_name_);
      by &lvllist; 
      id &trtvar;
      var _pct;
      format &trtvar;
    run;
    %if %length(&lvl1anylbl) %then %do;
      data _tranpct;
        set _tranpct;
        if lvl1=:"ANY " then lvl1=&lvl1anylbl;
        if lvl1=" " then delete;
      run;
      proc sort data=_tranpct;
      by &lvllist;
      run;
    %end;
    proc sort data=_tran;
      by &lvllist;
    run;
    data _tran;
      merge &pvalds _tran _tranpct;
      by &lvllist;
      if _pct%sysfunc(compress(&trtord,%str(%'%"))) >= &minpct;
    run;
  %end;
  %else %do;
    %if %length(&pvalds) %then %do;
      proc sort data=_tran;
        by &lvllist;
      run;
      data _tran;
        merge &pvalds _tran;
        by &lvllist;
      run;
    %end;
  %end;
        


      /*----------------------------------------------*
          combine the AE terms into a single column 
       *----------------------------------------------*/ 

  %comblvls(dsin=_tran,dsout=_trancmb,lvls=&lvls,colw=&colw,split=&split,
           indent=&indent,hindent=&hindent,usecolon=&usecolon);



        /*-----------------------------------------*
                  Manipulate output dataset 
         *-----------------------------------------*/

  %*-- apply filter code if any --;
  %if %length(&filtercode) %then %do;
    data _trancmb;
      set _trancmb;
      %unquote(%qdequote(&filtercode));
    run;
  %end;


  %*- call external data manipulation macro if set -;
  %if %length(&extmacro) %then %do;
    %&extmacro;
  %end;


             /*----------------------------*
                 print the final dataset 
              *----------------------------*/ 

  %if &print NE N %then %do;

    %let trtw=%eval(%scan(&nfmt,1,.)+%scan(&pctfmt,1,.)+3);
    %if &events EQ Y %then %let trtw=%eval(&trtw+1+%scan(&evfmt,1,.));
    %if &pctsign EQ Y %then %let trtw=%eval(&trtw+1);

    %prntlvls(dsin=_trancmb,lvls=&lvls,colw=&colw,trtvars=&trtvars,
    breaklvl=&breaklvl,trtw=&trtw,spacing=&spacing,trtalign=&trtalign,
    trtlabel=&trtlabel,comblabel=&comblabel,split=&split,topline=&topline,
    pageline=&pageline,pageline1=&pageline1,pageline2=&pageline2,
    pageline3=&pageline3,pageline4=&pageline4,pageline5=&pageline5,
    pageline6=&pageline6,pageline7=&pageline7,pageline8=&pageline8,
    pageline9=&pageline9,pageline10=&pageline10,pageline11=&pageline11,
    pageline12=&pageline12,pageline13=&pageline13,pageline14=&pageline14,
    pageline15=&pageline15,pagemacro=&pagemacro,pvalvar=&pvalpr,
    endline=&endline,endline1=&endline1,endline2=&endline2,
    endline3=&endline3,endline4=&endline4,endline5=&endline5,
    endline6=&endline6,endline7=&endline7,endline8=&endline8,
    endline9=&endline9,endline10=&endline10,endline11=&endline11,
    endline12=&endline12,endline13=&endline13,endline14=&endline14,
    endline15=&endline15,endmacro=&endmacro);

    proc datasets nolist;
      delete &pvalds _patcnt _events _tranrdy _tran _trancmb;
    run;
    quit;

  %end;

  %goto skip;
  %exit: %put &err: (aetab) Leaving macro due to problem(s) listed;
  %skip:

  options &savopts;

%mend aetab;