ABSTRACT Indenting with Style Bill Coar, Axio Research, Seattle, WA Within the pharmaceutical industry, many SAS programmers rely heavily on Proc Report. While it is used extensively for summary tables and listings, it is more typical that all processing is done prior to final report procedure rather than using some of its internal functionality. In many of the typical summary tables, some indenting is required. This may be required to combine information into a single column in order to gain more printable space (as is the case with many treatment group columns). It may also be to simply make the output more aesthetically pleasing. A standard approach it to pad a character string with spaces to give the appearance of indenting. This requires pre-processing of the data as well as the use of the ASIS=ON option in the column style. While this may be sufficient in many cases, it fails for longer text strings that require wrapping within a cell. Alternative approaches that conditionally utilize INDENT and LEFTMARGIN options of a column style are presented. This Quick-tip presentation will describe such options for indenting. Example outputs will be provided to demonstrate the pros and cons of each. The use of Proc Report and ODS is required in this application. The examples below were produced using SAS 9.4 in a Windows environment. INTRODUCTION Although the setting for the application presented may be quite specific, it is not restricted to the pharmaceutical industry. While there may be many individual circumstances that might lead a programmer to indent in a final report, there are two in particular that are most commonly used by the author. First, indenting for aesthetics which is quite common in typical summaries of safety data (for example, adverse events). The second is closely tied to aesthetics in that indenting is used to gain column space when many columns exist on a single page. The remainder of this paper will focus on these two circumstances when ODS destination is either RTF or PDF. MOTIVATING EXAMPLE In a clinical trial investigating the safety and effectiveness of a new or existing therapy, there may be multiple doses of a drug, and/or multiple drugs. In early development of an investigational therapy, dose ranging studies may be conducted to identify the doses most likely to result in a safe and effective dose to further examine in larger studies. Dose ranging studies can often result in many different doses to summarize, and can easily require excessive column space on a page. Table 1 via ODS PDF is an example of a typical adverse events summary table. When study participants take investigational drug, adverse reactions sometimes occur. These adverse reactions are mapped to standard terminology called a Preferred Term using a medical dictionary. Furthermore, Preferred Terms are grouped according to a body system (System Organ Class) affected by the event. This helps when trying to assess adverse reactions at a higher level. The typical summary in Table 1 summarizes the counts/percents of patients that experience at least 1 event for each Preferred Term observed as well as the counts/percents of patients that experience at least 1 event in each body system. System Organ Class and Preferred Term are often collapsed into a single character field for summarization. In the case where there are many treatment groups resulting from many doses used, it can be seen that the column space for the left most column needs to be reduced. 1
Table 1: Adverse Events by SOC and PT In this example, only 30% of the possible column space can be allocated to the text for System Organ Class (SOC) and Preferred Term (PT). In this typical table that reports the incidence of adverse events, indenting is used for aesthetics so that the reader understands that indented rows represent a dictionary preferred term. Rows associated with preferred terms are indented 10 spaces. At first, the existing technique (Option 1 below) for indenting seems adequate. However, problems arise when the length of the character term in the first column becomes excessive as is the case when Preferred Terms are lengthy. Examples will be presented below as each option for indenting is discussed. OPTION 1: PAD WITH SPACES A commonly used technique is to pad a character field in the first or second column with blank spaces. When there exist other (numeric) sorting variables, the character field can be displayed to give the appearance of indenting. When using an ODS destination with Proc Report, the character field is a DISPLAY variable defined using the ASIS=ON option when defining the column style. A screenshot of a sample dataset using this technique can be seen in Sample Data 1. Dataset 1: Padding with Spaces It is clear to see that there are two numeric sorting variables (SV1, SV2), a character string to display (ROW1), and various columns to display summary counts. Whenever SV2=0, the data represent counts with respect to a System Organ Class (SOC); otherwise, the row represents a Preferred Term (PT). Sample Code 1a shows an example IF statement that simply pads the character variable ROW1 with leading spaces depending on the value of a numeric sort variable SV2. Be careful that you do not truncate inadvertently due to the length of the character field being effected by the leading spaces. 2
Length row1 $200; If SV2>0 then row1= rowtxt; Sample Code 1a: Padding a character field with spaces By viewing Dataset 1, it is clear that there are leading spaces for ROW1 where rows are to appear indented. When using Proc Report with an ODS destinations such as RTF or PDF, leading spaces are ignored by default. To avoid this, the ASIS option in the column style definition can be used as shown in Example 2. define row1 / display left ' ' style(header)=[just=l] style(column)=[cellwidth=30% ASIS=on ]; Sample Code 1b: Use of ASIS option in a DEFINE statement The resulting output using this technique can be found in the motivating example Table 1. The technique of padding with spaces is not new. Its existence precedes both Proc Report as well as ODS. While it continues to be a viable technique, there are cases when it is insuffictient. In particular, when text wraps within a cell, the text wraps to the original left margin, as it should, leaving an undesirable appearance since the text is not in alignment as seen in Table 2a, the motivating example for Option 2. OPTION 2: PAD WITH SPACES + ~M We see in Table 1 via ODS PDF that there is limited column space to display the SOC and PT. Inevitably one will have data with a PT that is longer than the cell is wide which results in wrapping within the cell. Padding with blank spaces can result in wrapping text causing an undesirable appearance, as seen in Table 2a. Table 2a: Undesirable wrapping with padding The undesirable appearance is a function of using spaces to give the appearance of indenting. The cell is not actually indented. Thus, when text needs to wrap, it wraps to the original left margin of the cell. Using the ODS escape character (assumed to be ~ here), ~m is an escape sequence that will not print, but will mark the place where subsequent line breaks should wrap. The escape character can easily be added to the programming statement that pads with leading spaces as seen in Example 2. 3
Length row1 $200; If SV2>0 then row1= ~m rowtxt; Example 2: Padding with spaces + ~m A screenshot of data using this technique can be seen in Dataset 2. Dataset 2: Using ~m to control wrapping The result is shown in Table 2b via ODS PDF where we see more desirable wrapping with long text fields. Table 2b: Using ~m to mark where text wraps. While this option may be adequate for some, the downside is that it does not work with the RTF destination. My condolences to the RTF users reading this paper. OPTION 3: USE OF NBSPACE Rather than padding the character field with spaces, specifying the number of spaces with NBSPACE is an available option. Example 3 provides an example where text will be indented 10 spaces using NBSPACE. Length row1 $200; If SV0 >0 then row1= ~{NBSPACE 10} rowtxt; Example 3: In-line programming with NBSPACE The resulting data for reporting appears in Dataset 3. 4
Dataset 3: Using NBSPACE with ODS escape character Using this technique (with PDF), the output would appear the same as in Table 1. This option has the same restrictions as Option 1 as it may leave undesirable text wrapping. It may also require the use of ASIS=ON if the ODS destination is RTF. Ten (10) spaces was selected for demonstration only. It is not a restriction since NBSPACE is simply a way of specifying some number of spaces. OPTION 4: COMPUTE BLOCK + STYLE INDENT Alternatives to pre-processing text fields exist through the use of COMPUTE blocks. For these options, the user need not pad the text of ROW1 with spaces or escape characters. The data used for Options 4 and 5 are found in Dataset 4. Dataset 4: No indenting or pre-processing As seen in Dataset 4, there is no pre-processing of the text fields to give an appearance of indenting. Rather, options for changing the attributes of the cell to be displayed are modified. This technique will use a CALL DEFINE statement within a COMPUTE block to refine the style of a particular cell if certain conditions are met. To use this technique, the user needs to know which rows are to be indented apriori. When using the COMPUTE block option, three things are required. First, the variable used to identify which rows to indent needs to be added to the COLUMN statement. This variable is not displayed. Second, a DEFINE statement exists for this variable, which is often a sorting variable. Note that the NOPRINT option is used to indicate this variable should not be displayed. Third, a COMPUTE block is added to conditionally define the column style to specify the indentation as seen in Example 4. 5
column sv1 sv2 row1 _1 _2 _3 _4 _5 _6; define sv1/ order order=internal noprint ; define sv2/ order order=internal noprint ; define row1 / display left ' ' style(header)=[just=l] style(column)=[cellwidth=30% ]; define _1 / display center "Arm A (N=&ntrt1)" style(column)=[cellwidth=11% vjust=top];.. compute row1; if sv2 =0 then do; call define(_col_, "style", "style=[indent=0 cellwidth=30%]"); end; else do; call define(_col_, "style", "style=[indent=3% cellwidth=30%]"); end; endcomp; Example 4: Compute block with Style INDENT To aid in vertical alignment of the remaining columns, the VJUST=TOP option of the column style may be considered. While this option certainly has shown to be useful, it also may result in undesirable alignment when long character strings wrap as seen in Option 1 and Option 3. OPTION 5: COMPUTE BLOCK + STYLE LEFTMARGIN To alleviate the alignment problem that may occur in Option 4, the style option INDENT can be replaced by LEFTMARGIN. This is a straightforward update to the code provided in Example 4. With this option, proper alignment is achieved when long text strings wrap as seen in Table 5. Three percent (3%) was chosen for demonstration purposes as it results in an adequate amount of indenting for the report. Table 5: Using a COMPUTE block with LEFTMARGIN CONCLUDING REMARKS Five options for indenting have been presented. All have been used for data presentations by the author. Some 6
options such as Option 5 may be more general, but may have limitations as a function of the reporting dataset structure. Remember, SAS needs to be able to easily identify which rows to indent. There are some display types that may not allow for this. Luckily, other options exist. When considering options for indenting, note that: Some are easier than others Some may be appropriate for one summary, but not for others Not all work with RTF (e.g., ~m) Always consider the vertical justification (VJUST) in treatment group columns Always consider whether ASIS=ON may be required As the final concluding mark, the user may find subtle differences between RTF and PDF when it comes to wrapping. The break point where text begins to wrap may differ. The author does not view this as a limitation as it is simply a function of the desired ODS destination. REFERENCES [1] http://support.sas.com/documentation/cdl/en/odsug/61723/html/default/viewer.htm#a002233270.htm [2] Lund, P. PDF Can be Pretty Darn Fancy - Tips and Tricks for the ODS PDF Destination SUGI 31, http://www2.sas.com/proceedings/sugi31/092-31.pdf ACKNOWLEDGMENTS The author would like to thank Marcia Craib and Darrin Despain for their careful review of this paper. The author would also like to acknowledge the many programmers publishing information on the internet, and apologizes to those not properly referenced above, citing the inherent nature of internet searching which makes it difficult to track so many individual ideas. CONTACT INFORMATION Your comments and questions are valued and encouraged. Contact the author at: William Coar, PhD Biostatistician/Director of Statistical Consulting Axio Research, LLC Seattle, WA 98121 Email: williamc@axioresearch.com SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. indicates USA registration. Other brand and product names are trademarks of their respective companies. 7