/*
/ Program : unistats.sas
/ Version : 6.13
/ Author : Roland Rashleigh-Berry
/ Date : 03-Aug-2008
/ Purpose : Clinical reporting macro to calculate proc univariate
/ statistics and category counts with percentages with optional
/ pvalues and optionally print a report.
/ SubMacros : %unimap %words %fmtord %vartype %varfmt %unipvals %quotelst
/ %zerogrid %allfmtvals %unicatrep %varlen %dequote %verifyb
/ %quotecnt %quotescan %noquotes %unicat2word %sortedby %match
/ %nodup %attrc %eqsuff (assumes %popfmt already run)
/ Notes : This macro is NO LONGER SUPPORTED on a free basis. Maintenance
/ on it is time-consuming and the author does not have the free
/ time to do this. You can use it for free but suggestions for
/ enhancements from free users will be ignored and bugs reported
/ by free users may take a long time to get resolved or perhaps
/ never will be. Bugs normally only get fixed when the author
/ encounters them while using this macro. A support contract is
/ possible and is especially important if you intend to run
/ this or any of the other clinical reporting macros in a
/ production environment. Some major changes might be made to
/ this macro in the future and only those with a support
/ contract with me will be given consideration on how the change
/ might effect their use of this macro in the workplace.
/
/ The values for nfmt=, stdfmt=, minfmt=, maxfmt= and meanfmt=
/ have to be set to keep decimal point alignment the same.
/ In this way, if nfmt=3. then meanfmt=5.1 (a length of 5) for
/ the extra decimal point and decimal place. Note that meanfmt
/ is the default format for all stats values without a known
/ format and not just the mean.
/
/ The formats nfmt=, minfmt= etc. will be applied to all numeric
/ variables. To adjust them individually (compared to these
/ settings) you can end the variable with /+1 (no spaces) to add
/ one decimal place to all values (has no effect on nfmt=) or
/ /-1 to substract one decimal place. /+2 will add two decimal
/ places etc.. If you ask for statistics name transposed data then
/ no adjustment will be done as only one format can be applied.
/ It only works for treatment arm transposed data and the maximum
/ length allowed for any format is the length of catnfmt= plus 8.
/ An additional "m" in the decimal place adjust will make the min
/ and max values use the same format as mean.
/
/ A global macro variable _statkeys_ is created that maps the
/ labels supplied to stats= to their resulting variable names.
/
/ You can request output datasets from this macro. The contents
/ of these datasets are described below.
/
/ These are the variables in the dsout= dataset (stats)
/ -----------------------------------------------------
/ &byvars: whatever you defined to the byvars= parameter
/ &trtvar: whatever you defined to the trtvar= parameter
/ _page: Will be all 1 unless variables have been assigned to the
/ page1/2/..vars= parameters
/ _varord: A sequence number for the variables defined to varlist=
/ _vartype: "C" or "N" depending whether the variable is numeric
/ or categorical. Note that numeric variables can be
/ categorical depending on the assigned format.
/ _varname: The name of the variable
/ _varlabel: The label of the variable.
/ _dpadj: Decimal point adjustment
/ _minmaxadj: Make decimal points of min and max the same as mean
/ _statord: A sequence number for the statistic (or category).
/ If catn= has a value then the sequence number for it
/ will be 0.5
/ _statname: The name of the statistic (or category value)
/ _statlabel: The label of the statistic (or category). For stats
/ this will be as you defined it for the stats= parameter.
/ _value: Value of the statistic (or count of the category)
/ _pct: Percentage for category (for stats it will be missing)
/ _dummy=0: Dummy variable you can use to "flatten" a proc report
/ by declaring it "group noprint" after the "across"
/ variables have followed other "group" variables.
/
/
/ These are the variables in the dspout= dataset (pvalues)
/ --------------------------------------------------------
/ &byvars: whatever you defined to the byvars= parameter
/ &trtvar=9999: or whatever value assing to ptrtval=
/ _page: Will be all 1 unless variables have been assigned to the
/ page1/2/..vars= parameters
/ _varord: A sequence number for the variables defined to varlist=
/ _vartype: "C" or "N" depending whether the variable is numeric
/ or categorical. Note that numeric variables can be
/ categorical depending on the assigned format.
/ _varname: The name of the variable
/ _varlabel: The label of the variable.
/ _pvalue: p-value
/ _test: Test used CHISQ/FISHER/ANOVA
/ _statord=1 for categorical variables (or 0.5 if catn= is set)
/ and for numeric variables it is the value
/ corresponding to the MEAN statistic.
/ _statname: The name of the statistic (or category value)
/ _statlabel: The label of the statistic (or category). For stats
/ this will be as you defined it for the stats= parameter
/ _dummy=0
/
/
/ These are the variables in the dstrantrt= dataset
/ (treatment variable transposed data)
/ -------------------------------------------------
/ &byvars: whatever you defined to the byvars= parameter
/ _page: Will be all 1 unless variables have been assigned to the
/ page1/2/..vars= parameters.
/ _varord: A sequence number for the variables defined to varlist=
/ _vartype: "C" or "N" depending whether the variable is numeric
/ or categorical. Note that numeric variables can be
/ categorical depending on the assigned format.
/ _varname: The name of the variable
/ _varlabel: The label of the variable
/ _dpadj: Decimal point adjustment
/ _minmaxadj: Make decimal points of min and max the same as mean
/ _statord: A sequence number for the statistic (or category).
/ If catn= has a value then the sequence number for it
/ will be 0.5
/ _statname: The name of the statistic (or category value)
/ _statlabel: The label of the statistic (or category). For stats
/ this will be as you defined it for the stats= parameter
/ _dummy=0
/ _indent=' '
/ TRT1, TRT2, TRT3, etc.: character formatted value(s) with
/ variable names that are constructed using the prefix
/ defined to tranpref= followed by actual treatment arm
/ values.
/
/ Output order for the above dataset will be in the order
/ variables have been listed above.
/
/
/ These are the variables in the dstranstat= dataset
/ (statistic name transposed data)
/ --------------------------------------------------
/ &byvars: whatever you defined to the byvars= parameter
/ &trtvar: whatever you defined to the trtvar= parameter
/ _page: Will be all 1 unless variables have been assigned to the
/ page1/2/..vars= parameters.
/ _varord: A sequence number for the variables defined to varlist=
/ _vartype: "C" or "N" depending whether the variable is numeric
/ or categorical. Note that numeric variables can be
/ categorical depending on the assigned format.
/ _varname: The name of the variable
/ _varlabel: The label of the variable
/ _dpadj: Decimal point adjustment
/ _minmaxadj: Make decimal points of min and max the same as mean
/ N, MIN, MEAN, MAX etc. or whatever you defined to the stats=
/ parameter. Note that the variable names follow the
/ proc univariate naming conventions which are as follows:
/ N, MIN, MEAN, MAX, STD, P1, P5, P10, Q1, MEDIAN, Q3, P90,
/ P95, P99 and the labels of these variables will be the
/ actual strings supplied to the stats= parameter.
/
/ Output order for the above dataset will be in the order
/ variables have been listed above.
/
/ Usage : %unistats(dsin=means,dsout=out,dspout=pout,trtvar=tmt,
/ varlist=val,stats=n mean min max std,pvarlist=val);
/
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ dsin Input dataset
/ varlist Variables to calculate stats/category counts for. Decimal
/ places can be adjusted by following the variable name with
/ /+1 etc. (add 1 decimal place), /m (make mean, max and min
/ have the same number of decimal places) and a variable label
/ can be declared in the form var1="variable one". If you
/ define variable labels then you must enclose the list in
/ %str() so that the equals signs don't get confused with
/ parameter assignments.
/ pvarlist Variables to calculate pvalues for (must also be defined to
/ varlist= )
/ pvalues=yes This gets passed onto the %unicatrep macro to tell it
/ whether to show a pvalues column or not if the variable
/ exists in its input dataset.
/ nparvarlist Variables that require a p-value based on a non-parametric
/ test (must also be defined to varlist= and pvarlist=).
/ exactvarlist Variables defined to nparvarlist= that require a p-value
/ based on a non-parameteric exact test.
/ chisqvarlist Categorical variables for the Chi-square test (must also be
/ defined to varlist= and pvarlist=)
/ fishervarlist Categorical variables for the Fisher's exact test (must also
/ be defined to varlist= and pvarlist=)
/ pvaltrtlist List of treatment arm values (separated by spaces) used for
/ p-value calculation (defaults to not using the value
/ assigned to _trttotstr_).
/ ptrtval=9999 Dummy additional treatment group to assign to the pvalue
/ dataset.
/ npvalstat=MEAN Statistic keyword to merge numeric pvalues with. Note that
/ for merging with paired statistic labels then the first
/ keyword should be used.
/ adjcntrvar Variable representing the centre to be used in adjustment
/ for centre effects in PROC GLM call. Only one variable
/ allowed. Terms will be generated in the model statement
/ for modelform=short as:
/ model response=trtcd centre /ss1 ss2 ss3 ss4;
/ or for modelform=long as:
/ model response=trtcd centre trtcd*centre /ss1 ss2 ss3 ss4;
/ You can use this parameter for a variable other than a
/ centre but note that whatever variable you choose, if it is
/ not a categorical or dichotomous variable suitable for use
/ in the CLASS statement of a PROC GLM call then you will need
/ to use the glmclass= parameter to supply the correct call.
/ cntrwarn=yes By default, warn if a centre effect and/or centre-by-
/ treatment effect is significant. For the short model as
/ described for the adjcntrvar= parameter, use the centre term.
/ For the long model, use the centre and centre-by-treatment
/ effect term.
/ cntrcond=LE 0.05 Condition for significance to apply to the centre effect
/ intercond=LE 0.1 Condition for significance to apply to the treatment*centre
/ interaction.
/ statsopdest Default is not to write the glm output to any destination.
/ You can set this to PRINT or a file such that PROC PRINTTO
/ understands it. Note that setting it to LOG will not work.
/ errortype=3 Default is to select the error type 3 (Hypothesis type)
/ treatment arm p-value from the ModelANOVA ODS dataset that
/ is output from the GLM procedure.
/ modelform=short Default is to generate the short form of the model statement
/ as described in the adjcntrvar= parameter description.
/ dsmodelanova Dataset to append the ModelANOVA datasets to generated by
/ PROC GLM.
/ hovwarn=yes Issue a warning message where the homogeneity of variances
/ shows a significant difference. This will only be done for
/ one-way ANOVA so if adjcntrvar= is set then the hov*=
/ parameters will be ignored. A NOTE statement will be
/ generated where appropriate if a warning is not issued.
/ hovcond=LE 0.05 Condition for meeting HoV significance
/ hovtest=Levene HoV test to use. You can choose between OBRIEN, BF and
/ Levene, Levene(type=square) and Levene(type=abs). Levene and
/ Levene(type=square) are the same. The Bartlett test is not
/ supported.
/ welch=no By default, do not calculate ANOVA p-values using the Welch
/ test for one-way ANOVA where the HoV condition for
/ significance is met. If Welch is used then the hovtest
/ defined to hovtest= will be employed in the MEANS statement.
/ ---------------------------------------------------------
/ For the following glm*= parameters it is possible to enclose
/ your code in single quotes and then you can use &respvar
/ (the response variable) and &trtvar (the treatment variable)
/ in your code without causing syntax errors.
/ glmclass GLM CLASS statement used to override the generated form.
/ The start word CLASS and ending semicolon will be generated.
/ glmmodel GLM MODEL statement used to override the generated form.
/ The start word MODEL and ending semicolon will be generated.
/ glmmeans GLM MEANS statement used to override the generated form.
/ The start word MEANS and ending semicolon will be generated.
/ glmweight GLM WEIGHT statement that can be added as an extra. The
/ start word WEIGHT and ending semicolon will be generated.
/ stats List of stats labels separated by spaces (allows for
/ footnote flags at end for unpaired statistics). These will
/ be mapped to statistics keywords and if not possible an
/ error message will be issued. You are allowed to pair stats
/ labels such as "Mean(SD)" and in this case the values will
/ be combined using the extra characters in the string as the
/ delimiters. Although, for paired stats, no spaces are
/ allowed, a space will be substituted for the "^" character
/ to allow you to add spaces to improve layout. Although, for
/ single statistics, an attempt is made to align all values
/ with the decimal point, this can not be done for paired
/ statistics. Note that for paired statistics you might have
/ to choose a value for mincolw= to prevent truncation of the
/ second displayed value or you could increase the length of
/ the format assigned to catnfmt= . This is explained in the
/ notes below.
/ trtvar Treatment arm variable (defaults to &_trtvar_ set up in the
/ %popfmt macro but must be set if %popfmt not called) which
/ must be a coded numeric variable or a short coded character
/ variable (typically one or two bytes with no spaces).
/ trtfmt Treatment arm format (defaults to &_trtfmt_ set up in the
/ %popfmt macro but must be set if %popfmt not called).
/ total=no By default, do not show total for all treatment arms
/ byvars By variable(s)
/ dsout=_unistats Output dataset (no modifiers - defaults to _unistats). If
/ you rename this then do not use an underscore prefix
/ otherwise it could be deleted.
/ dspout=_pvalues P-value output dataset name (no modifiers - defaults to
/ _pvalues)
/ dstrantrt Dataset name for treatment arm transposed data
/ (no modifiers).
/ dstranstat Dataset name for statistic transposed data (no modifiers)
/ unicatrep=no By default, do not produce a report for treatment arm
/ transposed data.
/ print=yes This is passed on to %unicatrep and if set to "no" (no
/ quotes) the .lst output will be suppressed. This is in
/ case you just want ODS output.
/ unicatstyle=1 Style to use for the %unicatrep macro. 1=indented
/ 2=separate columns.
/ varw=12 Width of variable label column for unicatstyle=2
/ unistatrep=no By default, do not produce a report for statistic name
/ transposed data (REPORT NOT AVAILABLE YET).
/ spantotal=yes For when unicatrep is called, trtlabel spans the total
/ column as well, if there.
/ tranpref=TRT Prefix for transposed treatment arm variable names
/ missing=yes By default, display counts of categorical values that are
/ missing using what is defined to misstxt= as the name.
/ misstxt=Not Recorded Text for missing categoricals
/ misspct=no By default, do not calculate and show percentages for
/ missing value categories. If set to "no" (the default)
/ then p-values will not be based on the missing category
/ count. For these missing values to not feature in the
/ percentage values for non-missing categories, use
/ pctcalc=cat so that the percentage is based on the non-
/ missing category count.
/ nopctcatlist Extra categories to exclude from the percentage calculation
/ and p-value calculations. The items should be quoted and
/ separated by spaces. Item text should match the case before
/ any lowercasing is done by this macro.
/ catn By default, do not show the "N" category count. Set this to
/ "n", "N" or "n(missing)" (not quoted) to activate it. Note
/ that if a left round bracket is detected then the number of
/ missing values will be shown in round brackets following
/ the "n" count. Although free text is allowed, the second
/ part of the expression will be shown as the number of
/ missing values. No other statistic is possible.
/ varordadd=0 Optional number to add to _varord sequence values. You
/ would only use this if you were making multiple calls to
/ %unistats and bringing together the output datasets.
/ -----------------------------------------------------
/ Note that the following three parameters determine the
/ minimum width of the columns for when %unicatrep is called,
/ although you can override this by setting mincolw= .
/ The minimum width of the column is the length of the
/ catnfmt plus the pctfmt plus 3 (plus 1 if pctsign=yes).
/ If you are using paired stats labels then you can either
/ set mincolw= to a suitable value or increase the catnfmt
/ format length to avoid columns for the paired stats being
/ truncated.
/ catnfmt=3. Format for category counts for tranposed data
/ pctfmt=5.1 By default, use the 5.1 format to display percentages. You
/ must give this format a length for a user format.
/ pctsign=no By default, do not show the percent sign for percentages
/ --------------------------------------------------------
/ Note that you would normally set nfmt= to the same as
/ catfmt= and make sure the other formats that follow it
/ match such that the number of digits to the left of the
/ decimal point matches that for nfmt= .
/ nfmt=3. Format for N statistic for tranposed data
/ stdfmt=6.2 Format for STD statistic for tranposed data
/ minfmt=5.1 Format for MIN statistic for tranposed data
/ maxfmt=5.1 Format for MAX statistic for tranposed data
/ meanfmt=5.1 Format for MEAN statistic and ALL OTHER statistics for
/ tranposed data.
/ pvalfmt=p63val. Default format (created inside this macro) for p-value
/ statistic for tranposed data (6.3 unless <0.001 or >0.999)
/ pvalmisstxt=" n/a" (quoted) Used in the internal p63val. format for assigning
/ a string when the p-value has a missing value.
/ pvalids=yes By default, attach p-value ids to the end of p-values as per
/ the list defined below.
/ fisherid=^ Symbol to suffix formatted p-values for the Fisher exact
/ test.
/ chisqid=~ Symbol to suffix formatted p-values for the Chi-square test
/ cmhid=$ Symbol to suffix formatted p-values for the CMH test
/ anovaid=# Symbol to suffix formatted p-values for the ANOVA
/ nparid=° Symbol to suffix formatted p-values for the non-parametric
/ Kruskal-Wallis test (or Wilcoxon rank sum test).
/ ----------------------------------------------------------
/ The following parameters allow you to control page breaks
/ so that you can keep blocks of stats together. Suppose you
/ were showing age race and sex and sex got its stats split
/ part on page one and the rest on page two then you could
/ specify "page1vars=age race, page2vars=sex," and so force
/ all the sex stats onto page two.
/ page1vars Page 1 marked variables for a future print
/ page2vars Page 2 marked variables for a future print
/ page3vars Page 3 marked variables for a future print
/ page4vars Page 4 marked variables for a future print
/ page5vars Page 5 marked variables for a future print
/ page6vars Page 6 marked variables for a future print
/ page7vars Page 7 marked variables for a future print
/ page8vars Page 8 marked variables for a future print
/ page9vars Page 9 marked variables for a future print
/ catlabel=" " For a print the default is for no category column label
/ varlabel=" " For a print the default is for no variable column label
/ catw By default this macro will assign a category column width
/ to meet the page width.
/ trtlabel Label for combined category counts and stats
/ topline=no Default is not to show a line at the top of the report. This
/ is the best setting for ODS RTF tables and the like.
/ pageline=no Default is not to show a line under the report on each page
/ pageline1-9 Additional lines (in quotes) to show at the bottom of each
/ page.
/ endline=no Default is not to show a line under the end of the report.
/ This is the best setting for ODS RTF tables and the like.
/ endline1-9 Additional lines (in quotes) to show at end of report
/ pgbrkpos=before Page break position defaults to before. This will allow
/ the endline1-9 to be put after the last item is displayed.
/ If set to "after" (no quotes) then endline1-9 will be forced
/ onto a new page.
/ trtspace=4 Default spaces to leave between treatment arm columns
/ trtw1-9 Widths for each treatment column. If left blank then this
/ will be calculated for you.
/ indent=4 Spaces to indent categories for report
/ split=@ Split character (no quotes) for proc report if requested
/ pctcalc=pop By default, category percentages are calculated on the basis
/ of treatment arm population. This can be changed to "cat"
/ (no quotes) to calculate percentages based on total category
/ counts.
/ allcatvars List of variables (separated by spaces) for which all
/ categories belonging to a format are displayed.
/ allcat=no By default, do not show all the possible format values
/ for categorical variables. If set to yes then this
/ overrides the allcatvars= setting.
/ lowcasevarlist For categorical variables, make sure the second character
/ onwards are displayed as lower case.
/ dpalign=yes Default is to align the decimal point for descriptive
/ statistics using the "A0"x character (non-breaking space).
/ Set this to "no" (no quotes) to disable this action.
/ pctwarn=yes By default, put out a warning message if a percentage is
/ greater than 100.01
/ out Named output dataset from proc report if required when
/ %unicatrep macro is called.
/ wordtabdest Destination for a Word-type table of cell values to copy and
/ paste into Word and turn into a table. This only works for
/ where you have treatment columns like %unicatrep reports.
/ Note that you can use the odsrtf= parameter to give you RTF
/ tables suitable for inclusion into Word documents. RTF
/ tables can easily be manipulated and edited by word
/ processors.
/ wordtabdlim=';' Delimiter to use for Word-type table of cell values (must be
/ quoted).
/ odsrtf Give the "file='filename' style=style" (unquoted) to create
/ RTF output. This will be passed to the %unicatrep macro.
/ "ods rtf ;" and "ods rtf close;" will be automatically
/ generated. If you want to suppress the plain text output
/ then use the setting print=no. Use of topline=no (the
/ default) is advised if producing non-listing ODS output.
/ odshtml Works the same way as odsrtf
/ odshtmlcss Works the same way as odsrtf
/ odscsv Works the same way as odsrtf
/ odspdf Works the same way as odsrtf
/ odslisting Allows you to specify a file= statement to pass through to
/ the %unicatrep macro.
/ odsother Works the same way as odsrtf but you have to supply the
/ destination word as the first word.
/ tfmacro Name of macro (no % sign) containing titles and footnotes
/ settings to be enacted near the end of the macro if set and
/ will typically be used to include calculated p-values in
/ footnotes such as _pvalue1_ , _pvalue2_ etc. Use pvalues=no
/ to suppress the pvalues column in %unicatrep output if you
/ are displaying all the pvalues in footnotes.
/ byrowvar This is for when you want to show a "by" variable as a row
/ following the variable you are analyzing.
/ byroword Ordering variable for byrowvar if needed
/ byrowlabel Label for byrowvar
/ byrowalign Alignment of byrow (left/center/right)
/ byrowfmt Format for byrow variable
/ byroww Width for byrowvar
/ byrow2var Second byrow variable
/ byrow2ord Second byrow ordering variable if needed
/ byrow2label Second byrow label
/ byrow2align Second byrow alignment (left/center/right)
/ byrow2fmt Second byrow format
/ byrow2w Width for byrow2var
/ nbspaces=no By default, do not translate normal spaces to non-breaking
/ spaces. Setting it to "yes" (no quotes) can be useful for
/ maintaining alignment and forcing excel to treat cell
/ contents as text. This will be applied to stats output only.
/ To apply to other variables, use filtercode= and translate
/ spaces to "A0"x in the code you supply.
/ compskip=no (no quotes) By default, do not use a compute block for line
/ skips. Set this to "yes" only for non-paging ODS output such
/ as html so that you get an effect like "break after / skip"
/ showing blank lines in the output. Never set to "yes" for
/ paginated output.
/ compskippos=before When to throw the line skips
/ byrowfirst=no (no quotes) Default is not to display the variable declared
/ to byrowvar= as the first column when %unicatrep called.
/ mincolw Minimum width of the treatment columns. If you are using
/ paired stats then you can set this to a value to avoid
/ truncation.
/ trtvarlist You should not need to use this except in special situations
/ where you want to specify labels to go with the transposed
/ treatment variables in the proc report call at the end of
/ the %unicatrep macro such as in this example:
/ trtvarlist=("__ DRUG 1 __" TRT1 TRT2) ("__ DRUG 2 __" TRT3 TRT4)
/ You need to know what treatment variables there are and
/ this will be put in the log by the %popfmt macro. If you
/ are specifying column widths (usually not required) then if
/ you change the natural order of these TRT variables then the
/ trtw(n)= value that acts on a variable is the one that you
/ would use if the natural order had not been changed.
/ dsdenom Dataset to use for the denominator for calculating
/ percentages. Setting this overrides the pctcalc= process.
/ The denominator value must be held in the variable _total.
/ The sort order of this dataset will be used for the merge
/ but if no sort order is known then a matching of variable
/ names will be done. Note that if you have "by" variables
/ then these should normally be in this dataset as well.
/ denomshow=yes By default, show the denominator value (ddd) held in the
/ dsdenom dataset as part of the display string. The digits
/ will be trimmed and will have one space on each side. A
/ slash will precede it as shown below.
/ nnn / ddd (pct)
/ addnpct=no By default, do not add a string to the end of a categorical
/ variable label that indicates that the statistics are of the
/ "n (%)" type. Note that if all the variables are categorical
/ variables it is best to signify this in a title or add it
/ using the freesuff= parameter of the %popfmt macro.
/ addnpctstr=", n (%)" (quoted) String to add to the trimmed end of a
/ categorical variable label to indicate that the statistics
/ are of the "n (%)" type.
/ filtercode SAS code you specify to drop observations and do minor
/ reformatting before %unicatrep is called. If this code
/ contains commas then enclose in quotes (the quotes will be
/ dropped from the start and end before the code is executed).
/ Using this parameter might be easier than deferring the call
/ to %unicatrep and editing the dataset before calling
/ %unicatrep but for serious editing you should use the latter
/ method.
/ ============================================================
/ Note that the following parameters apply to ODS output only
/ spanrows=yes Applies to sas v9.2 and later. Default is to enable the
/ "spanrows" option for "proc report". You should leave this
/ set to "yes" (no quotes) unless you have a clear need.
/ ============================================================
/ Note that giving incorrect parameter values to fonts or any
/ other values used in a style() statement in proc report can
/ result in extremely confusing log error messages, usually
/ shown as a missing round bracket sign as explained here.
/ ERROR 79-322: Expecting a (.
/ ERROR 76-322: Syntax error, statement will be ignored.
/ http://support.sas.com/kb/2/457.html
/ There is nothing wrong with this macro if this happens. You
/ just need to get your values right. Whatever style()
/ statement causes an error message is the one that contains
/ an incorrect value. Check in the SAS documentation for the
/ values you are allowed to use. It won't be font weight or
/ style causing the problem as correct values for these are
/ enforced by this macro.
/ font_face_stats=courier Font to use for ODS output of the calculated stats
/ values. For correct alignment of the decimal point for non-
/ paired stats for MS Office then this should be set to
/ "Courier" (no quotes). If your font face contains more than
/ one word then use quotes (e.g. "courier new"). Also use
/ quotes if you are specifying a search order for fonts such
/ as "arial, helvetica".
/ font_weight_stats=medium Weight of font to use for stats columns
/ (choice of light, medium or bold enforced by this macro).
/ font_weight_other=bold Weight of font to use for columns other than
/ the calculated stats.
/ font_face_other=times Font to use for ODS output for columns other
/ than the calculated stats.
/ font_style_other=roman Font style (italic, roman or slant) to use for ODS
/ output for columns other than the calculated stats.
/ font_weight_other=bold Weight of font to use for columns other than
/ the calculated stats (choice of light, medium or bold).
/ report_border=no Whether to draw a border around the report
/ header_border=no Whether to draw a border around the column headers
/ column_border=no Whether to draw a border around the cells in the report
/ lines_border=no Whether to draw a border around the computed lines in the
/ report.
/ rules=none Ruler lines to use in the report must be set to one of the
/ following: none, all, cols, rows, groups.
/ cellspacing=0 Spacing between cells
/ cellpadding=0 Padding between cells
/ outputwidthpct=calc Integer percentage of the ODS output width to use.
/ Default is "calc" (no quotes) for the %unistats macro to
/ calculate a suitable value itself (ReportWidth*2/3).
/ Set to null (nothing) for ODS to do automatic sizing. This
/ won't look nice as there will be insufficient space between
/ columns with the default cellspacing=0. Set to whatever
/ integer percentage you like, otherwise. If this is set to
/ anything other than null then the column widths for ascii
/ reports are used to calculate percentages for individual
/ columns (if these are available).
/ ============================================================
/ Colors for the different parts of the table
/ background_header=white
/ foreground_header=black
/ background_stats=white
/ foreground_stats=black
/ background_other=white
/ foreground_other=black
/ ============================================================
/ Whether to draw underlines (_ul) or overlines (_ol) for
/ different parts of the report.
/ header_ul=yes
/ report_ul=yes
/ report_ol=yes
/ linepx=1 Width of drawn line in pixels
/ linecol=black Color of drawn line
/ compskip_ul=no Default is not to underline the compskip extra lines
/ compskippx=1 Width in pixels of the compskip underlines
/ compskipcol=black Color of the compskip underlines
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ rrb 12Jul05 v1.1 - parameters spantotal, breakvar, breakpos and
/ pageline2-5 added.
/ rrb 13Jul05 v2.0 allcatvars= and allcat= parameters added so that all
/ categories belonging to a format are displayed for these
/ variables.
/ rrb 09Jan06 v3.0 ability to adjust decimal places for numeric
/ variables added and numeric p-values have a _statord
/ value corresponding to the _statord of the MEAN.
/ rrb 24Jan06 Parameters nparvarlist=, chisqvarlist=, fishervarlist=
/ added to define type of p-value to calculate for listed
/ variables and pvatrtrlist= added so that treatment arm
/ values for the p-value calculation can be specified.
/ rrb 25Jan06 Global macro variables _pvalue1_ etc. set up to contain
/ pvalues.
/ rrb 30Jan06 lowcasevarlist= added and additional "m" allowed in
/ decimal places adjust to make min amd max the same format
/ as mean.
/ rrb 03Feb06 Default trtvar=
/ rrb 04May06 RRB001 Make pvartrtlist= default setting dependent on whether
/ pvarlist is set and put note in for trtfmt= to say it
/ must be set if %popfmt not called.
/ rrb 04May06 Only plug gaps with zeroes if treatment arm population
/ not zero.
/ rrb 04May06 Logic of plugging gaps with zeroes altered to use local
/ macro variable "plug".
/ rrb 07Jul06 Bug fix for catn=N processing plus some header tidies
/ rrb 10Jul06 misspct=yes bug fixed.
/ rrb 11Jul06 RRB002 _statord=1 set to get pvalues to merge with other data.
/ rrb 14Jul06 More header tidies
/ rrb 22Jul06 Do not lowercase _statlabel if set to "&misstxt"
/ rrb 26Aug06 v 3.1 has nopctcatlist= added so that no percentages are
/ calculated for these categories nor p-values.
/ rrb 23Sep06 v 3.2 has pvalmisstxt=" n/a" added
/ rrb 09Oct06 RRB003 Problem with missing fmtord. format fixed and minor
/ change to definition of p63val format.
/ rrb 13Feb07 "macro called" message added
/ rrb 12Mar07 Default now pctcalc=pop rather than pctcalc=cat (v3.3)
/ rrb 19Mar07 Add dpalign=yes parameter so that this action can be
/ disabled if required.
/ rrb 20Mar07 Handling of character trtvar corrected (v3.5)
/ rrb 21Mar07 Add STDMEAN handling and fix pvalue merge bug for
/ categorical variables (v3.6)
/ rrb 23Mar07 catnfmt= parameter can now have a format longer than
/ three characters (v4.0)
/ rrb 24Mar07 Use "A0"x instead of "FE"x for the decimal point
/ alignment character (v4.1)
/ rrb 25Mar07 _strlen_ set up to keep the string length for transposed
/ treatment variables for %unicatrep deferred calls (v4.2)
/ rrb 02May07 pctsign=no parameter added to allow users to force the
/ display of the percent sign for category percentages if
/ required.
/ rrb 16May07 pctfmt=5.1 parameter added to allow users to change the
/ percent format.
/ rrb 17May07 Further checking of pctfmt= value added (v4.5)
/ rrb 28May07 Allow the variable list to have variable labels attached
/ in the form var1="variable one" and apply these labels
/ to the output dataset. If followed by /n then the
/ variable will be treated as numeric for numeric
/ variables with a user format applied (v4.6)
/ rrb 30Jul07 Header tidy
/ rrb 01Sep07 pctwarn=yes parameter added
/ rrb 16Sep07 Added out= parameter to name the kept dataset created by
/ proc report to pass through to the %unicatrep macro, if
/ called.
/ rrb 16Sep07 wordtabdest= and wordtabdlim= parameters added
/ rrb 18Sep07 unicatstyle= , varw= and odsrtf= parameters added
/ rrb 19Sep07 odshtml= and odspdf= parameters added
/ rrb 21Sep07 odsother= parameter added
/ rrb 23Sep07 Header tidy
/ rrb 24Sep07 tfmacro= and pvalues=yes parameters added
/ rrb 30Sep07 topline=no is now the default which is better suited to
/ ODS output.
/ rrb 30Dec07 Delete _unidsin dataset just before creating new one and
/ fixed bug with _popfmt_ incorrectly assigned as a label
/ when trtfmt= set to otherwise.
/ rrb 30Dec07 Use nfmt as a format for the NMISS statistic
/ rrb 31Dec07 Mapping of statistic labels to keywords and formats is
/ displayed in log as a "note" statement. Keywords that
/ start with "STD" will have the stdfmt= format applied.
/ rrb 31Dec07 Bug fixed with NMISS format when decimal points adjusted
/ rrb 05Jan08 Decimal point alignment bug in "fillstr:" routine fixed
/ rrb 06Jan08 New parameters adjcntrvar= and statsopdest= added for pass
/ through to %unipvals for adjustment for centre effects.
/ rrb 06Jan08 Added errortype= , modelform= and dsmodelanova= for
/ pass-through to %unipvals for adjustment for centre
/ effects.
/ rrb 07Jan08 hovwarn=, hovcond=, hovtest= amd welch= added for pass-
/ through to %unipvals to test and warn for homogeneity of
/ variances significant difference for one-way ANOVA and to
/ calculate using Welch test. welchid=§ added in this macro
/ to give a symbol to a Welch p-value.
/ rrb 08Jan08 Added cntrwarn=, cntrcond= and cntrerrtype= for pass-
/ through to %unipvals for centre effect warning.
/ rrb 08Jan08 cntrerrtype= removed. cntrcond= value changed and
/ intercond=0.1 added.
/ rrb 13Jan08 dsmodelanova= dataset now deleted using proc datasets.
/ ftestid= replaced by anovaid=. cntrwarn= processing
/ changed.
/ rrb 13Jan08 Added cmhid= and support for the CMH test
/ rrb 14Jan08 glmclass=, glmmodel=, glmmeans= and glmweight= added so
/ the user can override the statements generated by the
/ %unipvals macro for the main glm call.
/ rrb 19Jan08 Added a note on use of glm*= parameters
/ rrb 19Jan08 Keep all variables in input dataset and pass all
/ variables to %unipvals.
/ rrb 20Jan08 Decimal places adjusted for a number of statistics
/ keywords.
/ rrb 26Jan08 Bug fixed with character format being like $3. in %cat
/ rrb 02Feb08 v5.0 allows for paired stats= words like "Mean(SD)" and
/ enhances the catn= value so that the number of missing
/ values can be shown. trtlabel= now defaults to null.
/ pvalids=yes added to allow suppression of all p-value ids
/ in the output. odshtmlcss= and odscsv= parameters added.
/ print=yes parameter added for pass-through to %unicatrep.
/ pluggaps= parameter added. byrow*= and compskip=
/ parameters added for pass-through to %unicatrep.
/ nbspaces= added to allow all spaces to be changed to non-
/ breaking spaces so that html output does not drop leading
/ spaces and convert multiple spaces to single spaces.
/ rrb 03Feb08 More byrow*= parameters added
/ rrb 09Feb08 compskip=no is now the default plus explanation of how to
/ increase column length for when paired stats are used has
/ been added to the header.
/ rrb 10Feb08 mincolw= parameter added
/ rrb 15Feb08 "^" now translated into a space when used in a delimiter
/ for paired stats. A space no longer automatically added
/ before a "(" and after a ";".
/ rrb 17Feb08 Added comments in header for page1vars= etc. parameters
/ rrb 23Feb08 byvars= generated where this is null but byrow*=
/ parameters used.
/ rrb 24Feb08 Numeric pvalue stat defined to npvalstat= is merged on
/ the first stat of a paired stat.
/ rrb 11Mar08 misspct= explanation updated in header
/ rrb 13Mar08 Mispelling of SKEWNESS corrected in code
/ rrb 15Mar08 odslisting= parameter added
/ rrb 17Mar08 byrowfirst= parameter added and breakvar= and breakpos=
/ parameters removed.
/ rrb 19Mar08 trtvarlist= parameter added
/ rrb 20Mar08 Where paired stats are both missing then show as blank
/ and not show delimiters.
/ rrb 25Mar08 If first of paired stats is "N" and this is missing but
/ second of pair is not missing then show "N" as "0".
/ rrb 25Mar08 For variables defined with a negative adjustment for
/ decimal places then the processing has changed such
/ that the min, max and sum can show zero decimal points in
/ addition to the variables that are counts (N, NMISS etc.)
/ but all other statistics must show at least one decimal
/ point.
/ rrb 23Apr08 Header changed to indicate that this macro is no longer
/ supported for free users (v6.0)
/ rrb 30Apr08 dsdenom= and denomshow= parameters added and glmopdest=
/ replaced by statsopdest= .
/ rrb 07May08 filtercode= parameter added
/ rrb 09May08 font_face_stats= and font_face_other= parameter added
/ rrb 09May08 report_border=, header_border=, column_border=, rules=
/ cellspacing= and lines_border= parameters added.
/ rrb 11May08 compskippos=, cellpadding= and outputwidthpct= parameters
/ added.
/ rrb 11May08 foreground and background parameters added
/ rrb 12May08 _ul and _ol parameters added
/ rrb 12May08 byvars2= parameter added for %unipvals call
/ rrb 13May08 font_style_other= and font_weight_stats parameters added
/ rrb 14May08 font_weight_stats= default changed to bold and
/ outputwidthpct= default changed to calc.
/ rrb 18May08 compskip_ul= compskippx= compskipcol= parameters added
/ rrb 21May08 addnpct= and addnpctstr= parameters added
/ rrb 02Jun08 spanrows= parameter added
/ rrb 03Aug08 Changed default to font_weight_stats=medium plus header
/ tidy.
/===============================================================================
/ No guarantee as to the suitability or accuracy of this code is given or
/ implied. User uses this code entirely at their own risk.
/=============================================================================*/
%put MACRO CALLED: unistats v6.13;
%macro unistats(dsin=,
varlist=,
pvarlist=,
pvalues=yes,
nparvarlist=,
exactvarlist=,
chisqvarlist=,
fishervarlist=,
pvaltrtlist=,
ptrtval=9999,
npvalstat=MEAN,
adjcntrvar=,
cntrwarn=yes,
cntrcond=LE 0.05,
intercond=LE 0.1,
statsopdest=,
errortype=3,
modelform=short,
dsmodelanova=,
hovwarn=yes,
hovcond=LE 0.05,
hovtest=Levene,
welch=no,
glmclass=,
glmmodel=,
glmmeans=,
glmweight=,
stats=,
trtvar=,
trtfmt=,
total=no,
byvars=,
dsout=_unistats,
dspout=_pvalues,
dstrantrt=,
dstranstat=,
unicatrep=no,
print=yes,
unicatstyle=1,
varw=12,
unistatrep=no,
spantotal=yes,
tranpref=TRT,
missing=yes,
misstxt=Not Recorded,
misspct=no,
nopctcatlist=,
catn=,
varordadd=0,
catnfmt=3.,
nfmt=3.,
stdfmt=6.2,
minfmt=5.1,
maxfmt=5.1,
meanfmt=5.1,
pctfmt=5.1,
pvalfmt=p63val.,
pvalmisstxt=" n/a",
pvalids=yes,
fisherid=^,
chisqid=~,
anovaid=#,
cmhid=$,
welchid=§,
nparid=°,
page1vars=,
page2vars=,
page3vars=,
page4vars=,
page5vars=,
page6vars=,
page7vars=,
page8vars=,
page9vars=,
catlabel=" ",
varlabel=" ",
catw=,
trtlabel=,
topline=no,
pageline=no,
pageline1=,
pageline2=,
pageline3=,
pageline4=,
pageline5=,
pageline6=,
pageline7=,
pageline8=,
pageline9=,
endline=no,
endline1=,
endline2=,
endline3=,
endline4=,
endline5=,
endline6=,
endline7=,
endline8=,
endline9=,
pgbrkpos=before,
trtspace=4,
trtw1=,
trtw2=,
trtw3=,
trtw4=,
trtw5=,
trtw6=,
trtw7=,
trtw8=,
trtw9=,
indent=4,
split=@,
pctcalc=pop,
allcatvars=,
allcat=no,
lowcasevarlist=,
dpalign=yes,
pctsign=no,
pctwarn=yes,
out=,
wordtabdest=,
wordtabdlim=';',
odsrtf=,
odshtml=,
odshtmlcss=,
odscsv=,
odspdf=,
odslisting=,
odsother=,
tfmacro=,
byrowvar=,
byroword=,
byrowlabel=" ",
byrowalign=,
byrowfmt=,
byroww=,
byrow2var=,
byrow2ord=,
byrow2label=" ",
byrow2align=,
byrow2fmt=,
byrow2w=,
pluggaps=yes,
nbspaces=no,
compskip=no,
compskippos=after,
byrowfirst=no,
mincolw=,
trtvarlist=,
filtercode=,
dsdenom=,
denomshow=yes,
addnpct=no,
addnpctstr=", n (%)",
spanrows=yes,
font_face_stats=courier,
font_weight_stats=medium,
font_face_other=times,
font_style_other=roman,
font_weight_other=bold,
report_border=no,
header_border=no,
column_border=no,
lines_border=no,
rules=none,
cellspacing=0,
cellpadding=0,
outputwidthpct=calc,
background_header=white,
foreground_header=black,
background_stats=white,
foreground_stats=black,
background_other=white,
foreground_other=black,
header_ul=yes,
report_ul=yes,
report_ol=yes,
linepx=1,
linecol=black,
compskip_ul=no,
compskippx=1,
compskipcol=black
);
%local i error j pg allvars var usefmtord fmt ls trtvartype libname memname plug
trtformat statfmts stat dpadj minmaxadj lowcase varlist2 usetest exact
strlen pctnfmt varlist3 varlist4 varlist5 forcenum denomsortvars;
%global _statkeys_ _strlen_;
%let _statkeys_=;
%global _misstxt_;
%let _misstxt_=&misstxt;
%*- we need this to get round the "BY-line truncated" bug in "proc univariate" -;
%let ls=%sysfunc(getoption(linesize));
/*-----------------------------------------*
Check we have enough parameters set
*-----------------------------------------*/
%let error=0;
%if not %length(&mincolw) %then %let mincolw=0;
%if not %length(&addnpct) %then %let addnpct=no;
%let addnpct=%upcase(%substr(&addnpct,1,1));
%if %length(&dsdenom) %then %let denomsortvars=%sortedby(&dsdenom);
%if not %length(&denomshow) %then %let denomshow=yes;
%let denomshow=%upcase(%substr(&denomshow,1,1));
%if not %length(&pluggaps) %then %let pluggaps=yes;
%let pluggaps=%upcase(%substr(&pluggaps,1,1));
%if not %length( aces) %then %let nbspaces=no;
%let nbspaces=%upcase(%substr( aces,1,1));
%if not %length(&pctsign) %then %let pctsign=no;
%let pctsign=%upcase(%substr(&pctsign,1,1));
%if not %length(&pctwarn) %then %let pctwarn=yes;
%let pctwarn=%upcase(%substr(&pctwarn,1,1));
%if "&pctwarn" EQ "N" %then %do;
%put WARNING: (unistats) Percent > 100.01 checks disabled;
%end;
%if not %length(&pctfmt) %then %let pctfmt=5.1;
%let pctnfmt=%substr(&pctfmt,%verifyb(&pctfmt,0123456789.)+1);
%if not %length(&pctnfmt) or not %index(&pctnfmt,.) %then %do;
%let error=1;
%put ERROR: (unistats) Format supplied to pctfmt=&pctfmt not valid;
%end;
%else %if "%substr(&pctnfmt,1,1)" EQ "." %then %do;
%let error=1;
%put ERROR: (unistats) No format length supplied to pctfmt=&pctfmt;
%end;
%if not %length(&pctcalc) %then %let pctcalc=pop;
%let pctcalc=%upcase(&pctcalc);
%if "&pctcalc" NE "CAT" and "&pctcalc" NE "POP" %then %do;
%let error=1;
%put ERROR: (unistats) You have pctcalc=&pctcalc but only CAT or POP are allowed;
%end;
%if not %length(&unicatrep) %then %let unicatrep=no;
%let unicatrep=%upcase(%substr(&unicatrep,1,1));
%if ("&unicatrep" EQ "Y" or %length(&wordtabdest))
and not %length(&dstrantrt) %then %let dstrantrt=_unitran;
%if "&unistatrep" EQ "Y" and not %length(&dstranstat)
%then %let dstrantrt=_unitranstat;
%if not %length(&minfmt) %then %let minfmt=&maxfmt;
%if not %length(&dsin) %then %do;
%let error=1;
%put ERROR: (unistats) No input dataset assigned to dsin=;
%end;
%if not %length(&varlist) %then %do;
%let error=1;
%put ERROR: (unistats) No variable list assigned to varlist=;
%end;
%else %do;
%*- set varlist3 to varlist but with labels and equals signs removed -;
%let varlist3=%sysfunc(compress(%noquotes(%str(&varlist)),=));
%*- set up varlist2 without decimal point adjust -;
%let varlist2=;
%do i=1 %to %words(&varlist3);
%if %length(&pvarlist) %then %do;
%global _pvalue&i._;
%let _pvalue&i._=;
%end;
%let var=%scan(&varlist3,&i,%str( ));
%*- drop the decimal point modifier if there is one -;
%let varlist2=&varlist2 %scan(&var,1,/);
%end;
%if not %length(&page1vars) %then %do;
%let page1vars=&varlist2;
%let page2vars=;
%let page3vars=;
%let page4vars=;
%let page5vars=;
%let page6vars=;
%let page7vars=;
%let page8vars=;
%let page9vars=;
%end;
%end;
%if not %length(&dsout) %then %do;
%let error=1;
%put ERROR: (unistats) No output dataset assigned to dsout=;
%end;
%if not %length(&trtvar) %then %do;
%let trtvar=&_trtvar_;
%put NOTE: (unistats) Defaulting trtvar=&trtvar;
%end;
%if not %length(&total) %then %let total=no;
%let total=%upcase(%substr(&total,1,1));
%if not %length(&misstxt) %then %let misstxt=Not Recorded;
%if not %length(&missing) %then %let missing=yes;
%let missing=%upcase(%substr(&missing,1,1));
%if not %length(&misspct) %then %let misspct=no;
%let misspct=%upcase(%substr(&misspct,1,1));
%if not %length(&allcat) %then %let allcat=no;
%let allcat=%upcase(%substr(&allcat,1,1));
%if "&missing" EQ "N" %then %let misspct=N;
%if not %length(&varordadd) %then %let varordadd=0;
%if %length(&pvarlist) %then %do; /* RRB001 */
%if not %length(&pvaltrtlist) %then %let pvaltrtlist=ne &_trttotstr_;
%else %let pvaltrtlist=in (&pvaltrtlist);
%end;
%if not %length(&trtfmt) %then %let trtfmt=&_popfmt_;
%if not %length(&dpalign) %then %let dpalign=yes;
%let dpalign=%upcase(%substr(&dpalign,1,1));
%if not %length(&pvalids) %then %let pvalids=yes;
%let pvalids=%upcase(%substr(&pvalids,1,1));
%if &error %then %goto error;
%*- set varlist4 to varlist but with the labels removed -;
%if %quotecnt(&varlist) %then %do;
%let varlist4=%noquotes(%str(&varlist));
%*- set varlist5 to the variables with labels defined -;
%let varlist5=;
%do i=1 %to %words(&varlist4);
%let var=%scan(&varlist4,&i,%str( ));
%let var=%scan(&var,1,/);
%if "%substr(&var,%length(&var),1)" EQ "="
%then %let varlist5=&varlist5 %substr(&var,1,%length(&var)-1);
%end;
%if %words(&varlist5) NE %quotecnt(&varlist) %then %do;
%let error=1;
%put ERROR: (unistats) Number of variable labels=%quotecnt(&varlist) does not;
%put ERROR: (unistats) match with variable count with labels=%words(&varlist5);
%put ERROR: (unistats) for varlist=&varlist;
%end;
%end;
%*----- work out the category display length -----;
%if not %length(&catnfmt) %then %let catnfmt=3.;
%*- add 3 for the space and round brackets -;
%let strlen=%eval(%scan(&catnfmt,1,.)+3);
%*- add the length of the percent format -;
%let strlen=%eval(&strlen+%scan(&pctnfmt,1,.));
%*- if displaying percent sign then add 1 to the length -;
%if "&pctsign" EQ "Y" %then %let strlen=%eval(&strlen+1);
%*- if displaying denominator then add more -;
%if %length(&dsdenom) and "&denomshow" EQ "Y"
%then %let strlen=%eval(&strlen+%scan(&catnfmt,1,.)+3);
%if &strlen LE &mincolw %then %let strlen=&mincolw;
%let _strlen_=&strlen;
%*- make sure the ModelANOVA append dataset is deleted -;
%if %length(&dsmodelanova) %then %do;
%if %sysfunc(exist(&dsmodelanova)) %then %do;
proc datasets nolist
%if %index(&dsmodelanova,.) %then %do;
lib=%scan(&dsmodelanova,1,.)
%end;
;
delete %scan(&dsmodelanova,-1,.);
run;
quit;
%end;
%end;
/*-----------------------------------------*
Check stat labels map to keywords
*-----------------------------------------*/
%if %length(&stats) %then %do;
%let _statkeys_=%unimap(&stats);
%if not %length(&_statkeys_) %then %goto error;
%put NOTE: (unistats) The following labels defined to stats: &stats;
%put NOTE: (unistats) were mapped to the following keywords: &_statkeys_;
%end;
/*-----------------------------------------*
Pair stats keywords to their format
*-----------------------------------------*/
%let statfmts=;
%if %length(&stats) %then %do;
%do i=1 %to %words(&_statkeys_);
%let stat=%scan(&_statkeys_,&i,%str( ));
%let statfmts=&statfmts &stat;
%if "&stat" EQ "N" %then %let statfmts=&statfmts &nfmt;
%else %if "&stat" EQ "NMISS" %then %let statfmts=&statfmts &nfmt;
%else %if "&stat" EQ "NOBS" %then %let statfmts=&statfmts &nfmt;
%else %if %index(&stat,STD) EQ 1 %then %let statfmts=&statfmts &stdfmt;
%else %if "&stat" EQ "KURTOSIS" %then %let statfmts=&statfmts &stdfmt;
%else %if "&stat" EQ "SKEWNESS" %then %let statfmts=&statfmts &stdfmt;
%else %if "&stat" EQ "MIN" %then %let statfmts=&statfmts &minfmt;
%else %if "&stat" EQ "MAX" %then %let statfmts=&statfmts &maxfmt;
%else %if "&stat" EQ "SUM" %then %let statfmts=&statfmts &maxfmt;
%else %let statfmts=&statfmts &meanfmt;
%end;
%put NOTE: (unistats) and have been assigned formats as follows: &statfmts;
%end;
/*-----------------------------------------*
Check all variables are present
*-----------------------------------------*/
%if not %length(&byvars) %then %let byvars=&byroword &byrowvar &byrow2ord &byrow2var;
%let allvars=&trtvar &byvars &adjcntrvar &varlist2;
%*- delete _unidsin dataset if it exists -;
%if %sysfunc(exist(_unidsin)) %then %do;
proc datasets nolist;
delete _unidsin;
run;
quit;
%end;
*- this data step in case input dataset has attached clauses -;
data _unidsin;
set &dsin;
%if %length(&trtfmt) %then %do;
format &trtvar &trtfmt ;
%end;
run;
%*- generate total for all treatment arms if requested -;
%if "&total" EQ "Y" %then %do;
data _unidsin;
set _unidsin;
output;
&trtvar=&_trttotstr_;
output;
run;
%end;
%*- check all variables are in input dataset -;
%do i=1 %to %words(&allvars);
%let var=%scan(&allvars,&i,%str( ));
%if not %varnum(_unidsin,&var) %then %do;
%let error=1;
%put ERROR: (unistats) Variable "&var" not in input dataset;
%end;
%end;
%if &error %then %goto error;
%*- if any variables labels found then apply to dataset -;
%if %quotecnt(&varlist) %then %do;
*- can not use "proc datasets" due to weird label syntax -;
data _unidsin;
set _unidsin;
label
%do i=1 %to %words(&varlist5);
%let var=%scan(&varlist5,&i,%str( ));
&var=%unquote(%quotescan(%str(&varlist),&i))
%end;
;
run;
%end;
%*- store the treatment variable type (C/N) -;
%let trtvartype=%vartype(_unidsin,&trtvar);
%*- store the treatment format -;
%let trtformat=%varfmt(_unidsin,&trtvar);
/*===========================================================*
*===========================================================*
Define macro to process CATegorical variables
*===========================================================*
*===========================================================*/
%macro cat(var,varnum,page,lowcase);
%local allfmtvals fmt varlen sortord;
%let allfmtvals=N;
%if %index(%quotelst(%upcase(&allcatvars)),"%upcase(&var)")
or "&allcat" EQ "Y" %then %let allfmtvals=Y;
%let fmt=%varfmt(_unidsin,&var);
%if not %length(%sysfunc(compress(&fmt,$1234567890.))) %then %do;
%let fmt=;
%let allfmtvals=N;
%end;
%let varlen=%varlen(_unidsin,&var,nodollar);
%if "%vartype(_unidsin,&var)" EQ "C" %then %do;
%if "%substr(&fmt%str( ),1,5)" EQ "$CHAR"
or not %length(%sysfunc(compress(&fmt,$123456789.)))
%then %let usefmtord=0;
%end;
%if %length(&fmt) %then %do;
%fmtord(&fmt);
%end;
data _unistatc;
length _statname $ 160
_varname $ 32
_varlabel $ 80
_dpadj $ 2
_minmaxadj $ 1
;
retain _page &page _varord %eval(&varnum+&varordadd) _vartype "C"
_varname "&var" _dpadj " " _minmaxadj " ";
set _unidsin;
_varlabel=vlabel(&var);
%if "&addnpct" EQ "Y" %then %do;
_varlabel=trim(_varlabel)||&addnpctstr;
%end;
_fmt=vformat(&var);
_vtype=vtype(&var);
if _fmt ne ' ' then do;
if _vtype='C' then do;
if &var NE ' ' then _statname=putc(&var,_fmt);
else _statname="&misstxt";
end;
else do;
if &var NE . then _statname=putn(&var,_fmt);
else _statname="&misstxt";
end;
end;
else _statname=&var;
run;
%*- if this is one of the pvalue variables then process -;
%if %index(%quotelst(%upcase(&pvarlist)),"%upcase(&var)") %then %do;
data __pin;
set _unistatc(where=(1=1
%if "&missing" EQ "Y" and "&misspct" EQ "N" %then %do;
and _statname not in ("&misstxt" &nopctcatlist)
%end;
));
run;
%let usetest=;
%if %index(%quotelst(%upcase(&chisqvarlist)),"%upcase(&var)")
%then %let usetest=C;
%else %if %index(%quotelst(%upcase(&fishervarlist)),"%upcase(&var)")
%then %let usetest=F;
%unipvals(dsin=__pin(where=(&trtvar &pvaltrtlist)),
dsout=__pout,trtvar=&trtvar,respvar=&var,type=C,usetest=&usetest,
adjcntrvar=&adjcntrvar,statsopdest=&statsopdest,errortype=&errortype,
modelform=&modelform,dsmodelanova=&dsmodelanova,
hovwarn=&hovwarn,hovcond=&hovcond,hovtest=&hovtest,welch=&welch,
cntrwarn=&cntrwarn,cntrcond=&cntrcond,intercond=&intercond,
byvars=&byvars,byvars2=_page _varord _vartype _varname _varlabel _dpadj _minmaxadj);
proc append base=&dspout data=__pout;
run;
proc datasets nolist;
delete __pin __pout;
run;
quit;
%end;
*- count of non-missing values -;
proc summary nway missing data=_unistatc
%if "&misspct" EQ "N" %then %do;
(where=(_statname not in ("&misstxt" &nopctcatlist)))
%end;
;
class &byvars &trtvar;
id _page _varord _vartype _varname _varlabel;
output out=_unistatt(drop=_type_ rename=(_freq_=_total));
run;
*- count of missing values -;
proc summary nway missing data=_unistatc
%if "&misspct" EQ "N" %then %do;
(where=(_statname EQ "&misstxt"))
%end;
;
class &byvars &trtvar;
id _page _varord _vartype _varname _varlabel;
output out=_unistatm(drop=_type_ rename=(_freq_=_pct));
run;
%if &pctcalc EQ CAT %then %let sortord=&byvars &trtvar;
%else %if &pctcalc EQ POP %then %let sortord=&trtvar &byvars;
proc summary nway missing data=_unistatc;
class &sortord _statname;
id _page _varord _vartype _varname _varlabel _dpadj _minmaxadj;
output out=_unistatc(drop=_type_ rename=(_freq_=_value));
run;
%if "&allfmtvals" EQ "Y" %then %do;
%allfmtvals(var=&var,length=&varlen,fmt=&fmt,dsout=_uniallfmt,decodevar=_statname,decodelen=160)
%zerogrid(dsout=_unizero,var1=&sortord _page _varord _vartype _varname _varlabel _dpadj _minmaxadj,
ds1=_unistatc,var2=_statname,ds2=_uniallfmt,zerovar=_value)
data _unistatc;
merge _unizero _unistatc;
by &sortord _statname;
run;
proc datasets nolist;
delete _unizero _uniallfmt;
run;
quit;
%end;
%if %length(&dsdenom) %then %do;
%if not %length(&denomsortvars) %then %do;
%let denomsortvars=%match(%varlist(_unistatc),%varlist(&dsdenom));
proc sort data=&dsdenom out=_unidenom;
by &denomsortvars;
run;
%end;
%else %do;
data _unidenom;
set &dsdenom;
run;
%end;
proc sort data=_unistatc;
by &denomsortvars;
run;
%end;
data _unistatc;
length _statlabel $ 160;
retain _dummy 0;
%if %length(&dsdenom) %then %do;
merge _unidenom _unistatc;
by &denomsortvars;
%end;
%else %if &pctcalc EQ CAT %then %do;
merge _unistatt _unistatc;
by &byvars &trtvar;
%end;
%else %if &pctcalc EQ POP %then %do;
merge _popfmt _unistatc(in=_data);
by &trtvar;
if _data;
format &trtvar &trtformat;
%end;
_statlabel=_statname;
%if "&lowcase" EQ "Y" %then %do;
if _statname NE "&misstxt" then
_statlabel=substr(_statlabel,1,1)||lowcase(substr(_statlabel,2));
%end;
if _statname in ("&misstxt" &nopctcatlist) then do;
if _statname EQ "&misstxt" then _statord=999;
else do;
%if %length(&fmt) %then %do;
_statord=input(_statname,fmtord.);
%end;
%else %do;
_statord=1; *- RRB003 -;
%end;
end;
_pct=.;
%if "&misspct" EQ "Y" %then %do;
_pct=100*_value/_total;
%if "&pctwarn" NE "N" %then %do;
if _pct GT 100.01 then
put "WARNING: (unistats) _pct GT 100.01 " (_all_) (=);
%end;
%end;
end;
else do;
%if %length(&fmt) %then %do;
_statord=input(_statname,fmtord.);
%end;
%else %do;
_statord=1; *- RRB002 -;
%end;
if _total in (.,0) then _pct=.;
else _pct=100*_value/_total;
%if "&pctwarn" NE "N" %then %do;
if _pct GT 100.01 then
put "WARNING: (unistats) _pct GT 100.01 " (_all_) (=);
%end;
end;
run;
proc sort data=_unistatc;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statord _statname;
run;
%if %length(&catn) %then %do;
*- merge the missing count with the total count -;
data _unistatt;
merge _unistatt _unistatm;
by &byvars &trtvar _page _varord _vartype _varname _varlabel;
if _total=. then _total=0;
if _pct=. then _pct=0;
run;
data _unistatt;
length _statlabel _statname $ 160 _dpadj $ 2 _minmaxadj $ 1;
retain _statord 0.5 _statname "N" _statlabel "&catn" _total .
_dummy 0 _vartype "C" _dpadj " " _minmaxadj " ";
set _unistatt(rename=(_total=_value));
run;
proc append base=&dsout data=_unistatt;
run;
%end;
proc append base=&dsout data=_unistatc
%if "&missing" EQ "N" %then %do;
(where=(_statname ne "&misstxt"))
%end;
;
run;
proc datasets nolist;
delete _unistatc _unistatt _unistatm;
run;
quit;
%mend cat;
/*===========================================================*
*===========================================================*
Define macro to process NUMeric variables
*===========================================================*
*===========================================================*/
%macro num(var,varnum,page,dpadj,minmaxadj);
%local i ;
data _unistatn;
length _varlabel $ 80
_varname $ 32
_dpadj $ 2
_minmaxadj $ 1
;
retain _page &page _varord %eval(&varnum+&varordadd) _vartype "N"
_varname "&var" _dpadj "&dpadj" _minmaxadj "&minmaxadj";
set _unidsin;
_varlabel=vlabel(&var);
run;
%*- if this is one of the pvalue variables then process -;
%if %index(%quotelst(%upcase(&pvarlist)),"%upcase(&var)") %then %do;
data __pin;
set _unistatn;
run;
%let usetest=;
%if %index(%quotelst(%upcase(&nparvarlist)),"%upcase(&var)")
%then %let usetest=N; %*- N=non-parametric test -;
%if %index(%quotelst(%upcase(&exactvarlist)),"%upcase(&var)")
%then %let exact=yes;
%unipvals(dsin=__pin(where=(&trtvar &pvaltrtlist)),
dsout=__pout,trtvar=&trtvar,respvar=&var,type=N,usetest=&usetest,
exact=&exact,adjcntrvar=&adjcntrvar,statsopdest=&statsopdest,
errortype=&errortype,modelform=&modelform,dsmodelanova=&dsmodelanova,
hovwarn=&hovwarn,hovcond=&hovcond,hovtest=&hovtest,welch=&welch,
cntrwarn=&cntrwarn,cntrcond=&cntrcond,intercond=&intercond,
glmclass=&glmclass,glmmodel=&glmmodel,glmmeans=&glmmeans,
glmweight=&glmweight,
byvars=&byvars,byvars2=_page _varord _vartype _varname _varlabel _dpadj _minmaxadj);
proc append base=&dspout data=__pout;
run;
proc datasets nolist;
delete __pin __pout;
run;
quit;
%end;
*- fix for "BYline truncated" bug is to set linesize to max -;
options ls=max;
proc univariate noprint data=_unistatn;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj;
var &var;
output out=_unistatn
%do i=1 %to %words(&_statkeys_);
%scan(&_statkeys_,&i,%str( ))%str(=)%scan(&_statkeys_,&i,%str( ))
%end;
;
run;
*- reset linesize -;
options ls=&ls;
proc transpose data=_unistatn out=_unistatn(drop=_label_
rename=(col1=_value));
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj;
var &_statkeys_;
run;
data _unistatn;
retain _pct _total . _dummy 0;
length _statname _statlabel $ 160;
set _unistatn;
_statname=_name_;
_statlabel=put(_statname,$_statlb.);
_statord=input(_statname,_stator.);
drop _name_;
run;
proc sort data=_unistatn;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statord _statname;
run;
proc append base=&dsout data=_unistatn;
run;
proc datasets nolist;
delete _unistatn;
run;
quit;
%mend num;
/*-----------------------------------------*
Create required formats
*-----------------------------------------*/
proc format;
value p63val
low-<0.001="<0.001"
0.999<-high=">0.999"
.=&pvalmisstxt
OTHER=[6.3]
;
invalue _stator
%do i=1 %to %words(&_statkeys_);
"%scan(&_statkeys_,&i,%str( ))"=&i
%end;
;
value $_statlb
%do i=1 %to %words(&_statkeys_);
"%scan(&_statkeys_,&i,%str( ))"="%scan(&_statlabs_,&i,%str( ))"
%end;
;
run;
/*-----------------------------------------*
Start processing the data
*-----------------------------------------*/
proc sort data=_unidsin;
by &byvars &trtvar;
run;
*- get rid of the append base datasets if they exist -;
proc datasets nolist;
delete &dsout
%if %length(&pvarlist) %then %do;
&dspout
%end;
;
run;
quit;
%do i=1 %to %words(&varlist3);
%let var=%scan(&varlist3,&i,%str( ));
%*- see if it had a decimal point adjust added -;
%let dpadj=%upcase(%scan(&var,2,/));
%*- see if treat-as-numeric is being asked for -;
%let forcenum=0;
%if %index(&dpadj,N) GT 0 %then %do;
%let forcenum=1;
%let dpadj=%sysfunc(compress(&dpadj,N));
%end;
%if %index(&dpadj,M) GT 0 %then %do;
%let minmaxadj=M;
%let dpadj=%sysfunc(compress(&dpadj,M));
%end;
%else %let minmaxadj=;
%if %length(&dpadj) %then %do;
%if "%substr(&dpadj,1,1)" NE "+"
and "%substr(&dpadj,1,1)" NE "-"
%then %let dpadj=+&dpadj;
%end;
%*- drop decimal point adjust from variable name -;
%let var=%scan(&var,1,/);
%let pg=99;
%do j=1 %to 9;
%if %index(%quotelst(%upcase(&&page&j.vars)),"%upcase(&var)") %then %do;
%let pg=&j;
%let j=99;
%end;
%end;
%if ("%vartype(_unidsin,&var)" EQ "C")
or ("%vartype(_unidsin,&var)" EQ "N" and not &forcenum and
("%substr(%varfmt(_unidsin,&var)%str( ),1,4)" NE "BEST"
and %length(%sysfunc(compress(%varfmt(_unidsin,&var),0123456789.)))))
%then %do;
%let lowcase=N;
%if %index(%quotelst(%upcase(&lowcasevarlist)),"%upcase(&var)") %then %let lowcase=Y;
%cat(&var,&i,&pg,&lowcase);
%end;
%else %num(&var,&i,&pg,&dpadj,&minmaxadj);
%end;
/*-----------------------------------------*
Add extra info to pvalues dataset
*-----------------------------------------*/
%if %length(&pvarlist) %then %do;
proc sort nodupkey data=&dsout(keep=&byvars _page _varord _varname _varlabel _vartype
_dpadj _minmaxadj _statord _statname _statlabel _dummy)
out=_pvalextra;
by &byvars _page _varord _varname _varlabel _dpadj _minmaxadj _statord;
run;
*- we need to include the lowest value of _statord for the categorical variables -;
data _pvalstatord;
set _pvalextra(where=(_vartype="C"));
by &byvars _page _varord _varname _varlabel _dpadj _minmaxadj;
if first._minmaxadj;
drop _statname _statlabel _dummy;
run;
*- sort ready for a merge to add _statord for categorical variables -;
proc sort data=&dspout;
by &byvars _page _varord _varname _varlabel _dpadj _minmaxadj;
run;
*- merge the lowest _statord value in for categorical variables -;
data &dspout;
merge &dspout(in=_p) _pvalstatord;
by &byvars _page _varord _varname _varlabel _dpadj _minmaxadj;
if _p;
run;
*- for numeric variables match with "mean" if possible or set _statord=1 -;
data &dspout;
retain &trtvar &ptrtval;
set &dspout;
if _vartype="N" then do;
_statord=input("%upcase(&npvalstat)",_stator.);
if _statord=. then _statord=1;
end;
format &trtvar &trtfmt;
run;
*- merge the extra variable in -;
data &dspout;
merge _pvalextra &dspout(in=_p);
by &byvars _page _varord _varname _varlabel _dpadj _minmaxadj _statord;
if _p;
run;
*- delete working datasets -;
proc datasets nolist;
delete _pvalextra _pvalstatord;
run;
quit;
%end;
/*-----------------------------------------*
Final output sort
*-----------------------------------------*/
proc sort data=&dsout;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statord _statname _statlabel;
run;
%if %length(&pvarlist) %then %do;
proc sort data=&dspout;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statord _statname _statlabel;
run;
%end;
/*---------------------------------------------------*
Transpose the unistats data using statistic name
*---------------------------------------------------*/
%if %length(&dstranstat) %then %do;
proc transpose data=&dsout out=&dstranstat(drop=_name_);
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj;
var _value;
id _statname;
idlabel _statlabel;
run;
%let libname=%scan(&dstranstat,-2,.);
%if not %length(&libname) %then %let libname=work;
%let memname=%scan(&dstranstat,-1,.);
*- assign the correct formats to the stats variables -;
%if %length(&stats) %then %do;
proc datasets nolist lib=&libname;
modify &memname;
format &statfmts;
run;
quit;
%end;
%end;
/*---------------------------------------------------*
Transpose the unistats data using treatment arm
*---------------------------------------------------*/
%if %length(&dstrantrt) %then %do;
%if %length(&pvarlist) %then %do;
*- format the p-values -;
data __pval;
length _pvalstr $ 8 _pvalmacvar $ &strlen _idlabel $ 120;
set &dspout;
_pvalmacvar="_pvalue"||compress(put(_varord,4.))||"_";
_pvalstr=put(_pvalue,&pvalfmt);
call symput(_pvalmacvar,trim(put(_pvalue,&pvalfmt)));
%if "&pvalids" NE "N" %then %do;
if _test="FISHER" then _pvalstr=trim(_pvalstr)||"&fisherid";
else if _test="CHISQ" then _pvalstr=trim(_pvalstr)||"&chisqid";
else if _test="CMH" then _pvalstr=trim(_pvalstr)||"&cmhid";
else if _test="ANOVA" then _pvalstr=trim(_pvalstr)||"&anovaid";
else if _test="WELCH" then _pvalstr=trim(_pvalstr)||"&welchid";
else if _test="NPAR1WAY" then _pvalstr=trim(_pvalstr)||"&nparid";
%end;
_idlabel=put&trtvartype(&trtvar,vformat(&trtvar));
drop _test _pvalue _pvalmacvar;
run;
%end;
%if %length(&pvarlist) %then %do;
%put MSG: (unistats) The following p-value global macro variables have;
%put MSG: (unistats) been set up and can be resolved in your code.;
%put MSG: (unistats) The index number corresponds to the _varord value.;
%do i=1 %to %words(&varlist);
%put _pvalue&i._=&&_pvalue&i._;
%end;
%put;
%end;
data &dstrantrt;
length _idlabel $ 120 _tempfmt $ 4 _str $ &strlen
;
set &dsout;
if _vartype="C" then do;
if (_statname in ("&misstxt" &nopctcatlist) and "&misspct" NE "Y")
then _str=put(_value,&catnfmt);
else if _statord=0.5 then do;
if not index("&catn",'(') then _str=put(_value,&catnfmt);
else _str=put(_value,&catnfmt)||' ('||trim(left(put(_pct,&catnfmt)))||')';
end;
else do;
%if "&pctsign" EQ "Y" %then %do;
%if %length(&dsdenom) and "&denomshow" EQ "Y" %then %do;
_str=put(_value,&catnfmt)||' / '||trim(left(put(_total, 8.)))||" ("||put(_pct,&pctfmt)||'%)';
%end;
%else %do;
_str=put(_value,&catnfmt)||' ('||put(_pct,&pctfmt)||'%)';
%end;
%end;
%else %do;
%if %length(&dsdenom) and "&denomshow" EQ "Y" %then %do;
_str=put(_value,&catnfmt)||' / '||trim(left(put(_total, 8.)))||" ("||put(_pct,&pctfmt)||')';
%end;
%else %do;
_str=put(_value,&catnfmt)||' ('||put(_pct,&pctfmt)||')';
%end;
%end;
end;
end;
else if _vartype="N" then do;
%if %length(&pvarlist) %then %do;
if _pvalstr ne ' ' then _str=_pvalstr;
else do;
%end;
if _dpadj=" " then do;
if _statname="N" then _str=put(_value,&nfmt);
else if _statname="NMISS" then _str=put(_value,&nfmt);
else if _statname="NOBS" then _str=put(_value,&nfmt);
else if _statname=:"STD" then _str=put(_value,&stdfmt);
else if _statname="KURTOSIS" then _str=put(_value,&stdfmt);
else if _statname="SKEWNESS" then _str=put(_value,&stdfmt);
else if _statname="MIN" then do;
if _minmaxadj NE "M" then _str=put(_value,&minfmt);
else _str=put(_value,&meanfmt);
end;
else if _statname="MAX" then do;
if _minmaxadj NE "M" then _str=put(_value,&maxfmt);
else _str=put(_value,&meanfmt);
end;
else if _statname="SUM" then do;
if _minmaxadj NE "M" then _str=put(_value,&maxfmt);
else _str=put(_value,&meanfmt);
end;
else _str=put(_value,&meanfmt);
end;
else do;
_tempadj=input(_dpadj,2.);
if _statname="N" then _str=put(_value,&nfmt);
else if _statname="NMISS" then _str=put(_value,&nfmt);
else if _statname="NOBS" then _str=put(_value,&nfmt);
else if _statname=:"STD" or _statname in ("KURTOSIS" "SKEWNESS") then do;
_tempfmt="&stdfmt";
link fillstr;
end;
else if _statname="MIN" then do;
if _minmaxadj NE "M" then _tempfmt="&minfmt";
else _tempfmt="&meanfmt";
link fillstr;
end;
else if _statname="MAX" then do;
if _minmaxadj NE "M" then _tempfmt="&maxfmt";
else _tempfmt="&meanfmt";
link fillstr;
end;
else if _statname="SUM" then do;
if _minmaxadj NE "M" then _tempfmt="&maxfmt";
else _tempfmt="&meanfmt";
link fillstr;
end;
else do;
_tempfmt="&meanfmt";
link fillstr;
end;
end;
%if %length(&pvarlist) %then %do;
end;
%end;
end;
%if  aces EQ Y %then %do;
_str=translate(_str,"A0"x," ");
%end;
%else %if &dpalign EQ Y %then %do;
if substr(_str,&strlen,1)=' ' then substr(_str,&strlen,1)="A0"x;
%end;
_idlabel=put&trtvartype(&trtvar,vformat(&trtvar));
return;
fillstr:
if input(scan(_tempfmt,2,"."),2.) not in (.,0) then do;
if _tempadj GT 0 then _tempfmt=compress(put(input(scan(_tempfmt,1,"."),2.)+_tempadj,2.))||"."||compress(put(input(scan(_tempfmt,2,"."),2.)+_tempadj,2.));
else do;
if input(scan(_tempfmt,2,"."),2.)+_tempadj LE 0 then do;
if _statname in ("MIN", "MAX", "SUM") then _tempfmt="&nfmt";
else _tempfmt="&meanfmt";
end;
else _tempfmt=compress(put(input(scan(_tempfmt,1,"."),2.)+_tempadj,2.))||"."||compress(put(input(scan(_tempfmt,2,"."),2.)+_tempadj,2.));
end;
end;
else do;
if _tempadj GT 0 then _tempfmt=compress(put(input(scan(_tempfmt,1,"."),2.)+1+_tempadj,2.))||"."||compress(put(_tempadj,2.));
end;
_str=putn(_value,_tempfmt);
return;
drop _tempfmt _tempadj;
run;
*- sort and combine values for combined stats labels such as "Mean(SD)" -;
proc sort data=&dstrantrt;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statlabel _statord;
run;
data &dstrantrt;
length _newstr _skelolabel $ &strlen
_delim1 _delim2 $ 4
_holdstatname $ 160
_holdstatord 8
;
retain _newstr _holdstatname " " _holdstatord .;
set &dstrantrt;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statlabel;
if first._statlabel and last._statlabel then output;
else do;
if first._statlabel then do;
_newstr=left(translate(_str," ","A0"x));
_holdstatname=_statname;
_holdstatord=_statord;
end;
if last._statlabel and not first._statlabel then do;
*- set "N" value to zero if this is missing -;
if _holdstatname="N" and _newstr=" " then _newstr="0";
_skelolabel=translate(upcase(_statlabel)," ","ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.");
_delim1=left(scan(_skelolabel,1," "));
_delim2=left(scan(_skelolabel,2," "));
_str=translate(_str," ","A0"x);
if not (_newstr=" " and _str=" ") then
_str=trim(_newstr)||trim(_delim1)||trim(left(_str))||_delim2;
else _str=" ";
_statname=_holdstatname;
_statord=_holdstatord;
*- translate hat character to a space -;
_str=translate(_str," ","^");
_newstr=" ";
output;
end;
end;
drop _newstr _skelolabel _delim1 _delim2 _holdstatname _holdstatord;
proc sort data=&dstrantrt;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statord _statname _statlabel;
run;
data &dstrantrt;
length _str $ &strlen;
%if %length(&pvarlist) %then %do;
merge __pval &dstrantrt;
by &byvars &trtvar _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statord _statname _statlabel;
if _pvalstr ne " " then _str=_pvalstr;
%end;
%else %do;
set &dstrantrt;
%end;
*- translate hat character to a space -;
if _vartype="N" then _statlabel=translate(_statlabel," ","^");
run;
*- sort ready for a transpose... -;
proc sort data=&dstrantrt;
by &byvars _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statord _statname _statlabel &trtvar;
run;
*- ...and then transpose -;
proc transpose data=&dstrantrt prefix=&tranpref out=&dstrantrt(drop=_name_);
by &byvars _page _varord _vartype _varname _varlabel _dpadj _minmaxadj _statord _statname _statlabel;
var _str;
id &trtvar;
idlabel _idlabel;
format &trtvar ;
run;
*- plug the gaps -;
data &dstrantrt;
length &_trtvarlist_ $ &strlen;
retain _dummy 0 _indent ' ';
set &dstrantrt;
%if "&pluggaps" EQ "Y" %then %do;
%do i=1 %to &_trtnum_;
%*- only plug gaps for when a treatment arm has a non-zero population -;
%if "%scan(&_trttotals_,&i,%str( ))" NE "0" %then %do;
*- test if we have spaces in this treatment arm column -;
if %scan(&_trtvarlist_,&i,%str( )) EQ " " then do;
if _vartype="C" then do;
if (_statname in (" " &nopctcatlist) or (_statname="&misstxt" and "&misspct" EQ "N")) then do;
%scan(&_trtvarlist_,&i,%str( ))=put(0,&catnfmt);
end;
else if _statname="N" then do;
*- for where you have a count combined with a missing count -;
if scan(_statlabel,2) EQ " " then %scan(&_trtvarlist_,&i,%str( ))=put(0,&catnfmt);
else %scan(&_trtvarlist_,&i,%str( ))=put(0,&catnfmt)||" (0)";
end;
else do;
%if "&pctsign" EQ "Y" %then %do;
%scan(&_trtvarlist_,&i,%str( ))=put(0,&catnfmt)||" ("||put(0,&pctfmt)||"%)";
%end;
%else %do;
%scan(&_trtvarlist_,&i,%str( ))=put(0,&catnfmt)||" ("||put(0,&pctfmt)||")";
%end;
end;
%if  aces EQ Y %then %do;
%scan(&_trtvarlist_,&i,%str( ))=translate(%scan(&_trtvarlist_,&i,%str( )),"A0"x," ");
%end;
%else %if &dpalign EQ Y %then %do;
if substr(%scan(&_trtvarlist_,&i,%str( )),vlength(%scan(&_trtvarlist_,&i,%str( ))),1) EQ " "
then substr(%scan(&_trtvarlist_,&i,%str( )),vlength(%scan(&_trtvarlist_,&i,%str( ))),1)="A0"x;
%end;
end; /* end of _vartype="C" */
else do; /* for _vartype="N" only plug the N or N(missing) value */
if _statname="NMISS" and scan(_statlabel,2) NE " " then
%scan(&_trtvarlist_,&i,%str( ))=put(0,&catnfmt)||" (0)";
else if _statname="N" then do;
%scan(&_trtvarlist_,&i,%str( ))=put(0,&catnfmt);
%if  aces EQ Y %then %do;
%scan(&_trtvarlist_,&i,%str( ))=translate(%scan(&_trtvarlist_,&i,%str( )),"A0"x," ");
%end;
%else %if &dpalign EQ Y %then %do;
if substr(%scan(&_trtvarlist_,&i,%str( )),vlength(%scan(&_trtvarlist_,&i,%str( ))),1)=" "
then substr(%scan(&_trtvarlist_,&i,%str( )),vlength(%scan(&_trtvarlist_,&i,%str( ))),1)="A0"x;
%end;
end;
end;
end;
%end;
%end;
%end;
label
%do i=1 %to &_trtnum_;
%scan(&_trtvarlist_,&i,%str( ))="%sysfunc(put&trtvartype(%dequote(%scan(&_trtinlist_,&i,%str( ))),
&trtfmt))"
%end;
;
run;
%*-- apply filter code if any --;
%if %length(&filtercode) %then %do;
data &dstrantrt;
set &dstrantrt;
%dequote(&filtercode);
run;
%end;
*- final sort -;
proc sort data=&dstrantrt;
by &byvars _page _varord _vartype _varname _varlabel _dpadj _minmaxadj
_statord _statname _statlabel _dummy _indent;
run;
%if %length(&pvarlist) %then %do;
proc datasets nolist;
delete __pval;
run;
quit;
%end;
%end;
/*-----------------------------------------*
Call titles and footnotes macro if set
*-----------------------------------------*/
%if %length(&tfmacro) %then %do;
%&tfmacro;
%end;
/*-----------------------------------------*
Call reporting macro if unicatrep is set
*-----------------------------------------*/
%if "&unicatrep" EQ "Y" %then %do;
%unicatrep(dsin=&dstrantrt,byvars=&byvars,
catlabel=&catlabel,catw=&catw,spantotal=&spantotal,pageline=&pageline,endline=&endline,
pageline1=&pageline1,pageline2=&pageline2,pageline3=&pageline3,pageline4=&pageline4,
pageline5=&pageline5,pageline6=&pageline6,pageline7=&pageline7,pageline8=&pageline8,
pageline9=&pageline9,pgbrkpos=&pgbrkpos,total=&total,strlen=&strlen,varlabel=&varlabel,
endline1=&endline1,endline2=&endline2,endline3=&endline3,endline4=&endline4,endline5=&endline5,
endline6=&endline6,endline7=&endline7,endline8=&endline8,endline9=&endline9,
trtlabel=&trtlabel,topline=&topline,trtspace=&trtspace,style=&unicatstyle,print=&print,
trtw1=&trtw1,trtw2=&trtw2,trtw3=&trtw3,trtw4=&trtw4,trtw5=&trtw5,trtw6=&trtw6,
trtw7=&trtw7,trtw8=&trtw8,trtw9=&trtw9,indent=&indent,split=&split,out=&out,varw=&varw,
odsrtf=&odsrtf,odshtml=&odshtml,odspdf=&odspdf,odsother=&odsother,odshtmlcss=&odshtmlcss,
odscsv=&odscsv,odslisting=&odslisting,pvalues=&pvalues,compskip=&compskip,
byrowvar=&byrowvar,byroword=&byroword,byrowlabel=&byrowlabel,compskippos=&compskippos,
byrowalign=&byrowalign,byrowfmt=&byrowfmt,byroww=&byroww,
byrow2var=&byrow2var,byrow2ord=&byrow2ord,byrow2label=&byrow2label,
byrow2align=&byrow2align,byrow2fmt=&byrow2fmt,byrow2w=&byrow2w,
byrowfirst=&byrowfirst,trtvarlist=&trtvarlist,font_face_stats=&font_face_stats,
font_face_other=&font_face_other,font_weight_other=&font_weight_other,
report_border=&report_border,header_border=&header_border,
column_border=&column_border,lines_border=&lines_border,rules=&rules,
cellspacing=&cellspacing,cellpadding=&cellpadding,outputwidthpct=&outputwidthpct,
background_header=&background_header,foreground_header=&foreground_header,
background_stats=&background_stats,foreground_stats=&foreground_stats,
background_other=&background_other,foreground_other=&foreground_other,
header_ul=&header_ul,report_ul=&report_ul,report_ol=&report_ol,
linepx=&linepx,linecol=&linecol,font_style_other=&font_style_other,
font_weight_stats=&font_weight_stats,compskip_ul=&compskip_ul,
compskippx=&compskippx,compskipcol=&compskipcol,spanrows=&spanrows);
%end;
/*-----------------------------------------*
Call conversion-to-Word-table macro
*-----------------------------------------*/
%if %length(&wordtabdest) %then %do;
%unicat2word(dsin=&dstrantrt,dest=&wordtabdest,dlim=&wordtabdlim,total=&total)
%end;
/*-----------------------------------------*
Exit
*-----------------------------------------*/
%goto skip;
%error:
%put ERROR: (unistats) Leaving macro due to error(s) listed;
%skip:
%mend;