Uncommon Techniques for Common Variables

Similar documents
SAS Certification Handout #10: Adv. Prog. Ch. 5-8

SQL Metadata Applications: I Hate Typing

Top 5 Handy PROC SQL Tips You Didn t Think Were Possible

Dictionary.coumns is your friend while appending or moving data

Know Thy Data : Techniques for Data Exploration

Edit the Editor: Creating Keyboard Macros in SAS Enterprise Guide

Taming a Spreadsheet Importation Monster

Same Data Different Attributes: Cloning Issues with Data Sets Brian Varney, Experis Business Analytics, Portage, MI

INTRODUCTION TO SAS HOW SAS WORKS READING RAW DATA INTO SAS

Abstract. Introduction. How Are All of These Tables Related? - Relational Database Map - RDB_MAP.SAS

Paper B GENERATING A DATASET COMPRISED OF CUSTOM FORMAT DETAILS

The Power of PROC SQL Techniques and SAS Dictionary Tables in Handling Data

Contents of SAS Programming Techniques

How to Keep Multiple Formats in One Variable after Transpose Mindy Wang

How to Create Data-Driven Lists

SAS Viya 3.1 FAQ for Processing UTF-8 Data

Better Metadata Through SAS II: %SYSFUNC, PROC DATASETS, and Dictionary Tables

Programming & Manipulation. Danger: MERGE Ahead! Warning: BY Variable with Multiple Lengths! Bob Virgile Robert Virgile Associates, Inc.

Know Thy Data: Techniques for Data Exploration

Cleaning Duplicate Observations on a Chessboard of Missing Values Mayrita Vitvitska, ClinOps, LLC, San Francisco, CA

Validation Summary using SYSINFO

Identifying Duplicate Variables in a SAS Data Set

Tweaking your tables: Suppressing superfluous subtotals in PROC TABULATE

DSCI 325: Handout 10 Summarizing Numerical and Categorical Data in SAS Spring 2017

So, Your Data are in Excel! Ed Heaton, Westat

DBLOAD Procedure Reference

Why choose between SAS Data Step and PROC SQL when you can have both?

Create Metadata Documentation using ExcelXP

A SAS Macro Utility to Modify and Validate RTF Outputs for Regional Analyses Jagan Mohan Achi, PPD, Austin, TX Joshua N. Winters, PPD, Rochester, NY

Open Problem for SUAVe User Group Meeting, November 26, 2013 (UVic)

Quick Data Definitions Using SQL, REPORT and PRINT Procedures Bradford J. Danner, PharmaNet/i3, Tennessee

Developing Data-Driven SAS Programs Using Proc Contents

Tales from the Help Desk 6: Solutions to Common SAS Tasks

Paper DB2 table. For a simple read of a table, SQL and DATA step operate with similar efficiency.

Adjusting for daylight saving times. PhUSE Frankfurt, 06Nov2018, Paper CT14 Guido Wendland

An Easy Route to a Missing Data Report with ODS+PROC FREQ+A Data Step Mike Zdeb, FSL, University at Albany School of Public Health, Rensselaer, NY

WHAT ARE SASHELP VIEWS?

Producing Summary Tables in SAS Enterprise Guide

Introduction to SAS Procedures SAS Basics III. Susan J. Slaughter, Avocet Solutions

Mimicking the Data Step Dash and Double Dash in PROC SQL Arlene Amodeo, Law School Admission Council, Newtown, PA

footnote1 height=8pt j=l "(Rev. &sysdate)" j=c "{\b\ Page}{\field{\*\fldinst {\b\i PAGE}}}";

Microsoft Access Illustrated. Unit B: Building and Using Queries

Tips & Tricks. With lots of help from other SUG and SUGI presenters. SAS HUG Meeting, November 18, 2010

A Better Perspective of SASHELP Views

Preserving your SAS Environment in a Non-Persistent World. A Detailed Guide to PROC PRESENV. Steven Gross, Wells Fargo, Irving, TX

Give me EVERYTHING! A macro to combine the CONTENTS procedure output and formats. Lynn Mullins, PPD, Cincinnati, Ohio

Paper S Data Presentation 101: An Analyst s Perspective

Formatting Highly Detailed Reports: Eye-Friendly, Insight-Facilitating

PhUSE US Connect 2018 Paper CT06 A Macro Tool to Find and/or Split Variable Text String Greater Than 200 Characters for Regulatory Submission Datasets

Chapter 2: Getting Data Into SAS

MOBILE MACROS GET UP TO SPEED SOMEWHERE NEW FAST Author: Patricia Hettinger, Data Analyst Consultant Oakbrook Terrace, IL

9 Ways to Join Two Datasets David Franklin, Independent Consultant, New Hampshire, USA

Exporting Variable Labels as Column Headers in Excel using SAS Chaitanya Chowdagam, MaxisIT Inc., Metuchen, NJ

Andrew H. Karp Sierra Information Services, Inc. San Francisco, California USA

Using PROC REPORT to Cross-Tabulate Multiple Response Items Patrick Thornton, SRI International, Menlo Park, CA

David Ghan SAS Education

An SQL Tutorial Some Random Tips

Square Peg, Square Hole Getting Tables to Fit on Slides in the ODS Destination for PowerPoint

Planting Your Rows: Using SAS Formats to Make the Generation of Zero- Filled Rows in Tables Less Thorny

Data Quality Review for Missing Values and Outliers

Essential ODS Techniques for Creating Reports in PDF Patrick Thornton, SRI International, Menlo Park, CA

EXAMPLE 2: INTRODUCTION TO SAS AND SOME NOTES ON HOUSEKEEPING PART II - MATCHING DATA FROM RESPONDENTS AT 2 WAVES INTO WIDE FORMAT

Syntax Conventions for SAS Programming Languages

Introduction to SAS Procedures SAS Basics III. Susan J. Slaughter, Avocet Solutions

A Side of Hash for You To Dig Into

TIPS FROM THE TRENCHES

Base and Advance SAS

Run your reports through that last loop to standardize the presentation attributes

Beginning Tutorials. Introduction to SAS/FSP in Version 8 Terry Fain, RAND, Santa Monica, California Cyndie Gareleck, RAND, Santa Monica, California

CC13 An Automatic Process to Compare Files. Simon Lin, Merck & Co., Inc., Rahway, NJ Huei-Ling Chen, Merck & Co., Inc., Rahway, NJ

BY S NOTSORTED OPTION Karuna Samudral, Octagon Research Solutions, Inc., Wayne, PA Gregory M. Giddings, Centocor R&D Inc.

Handling missing values in Analysis

Windows Application Using.NET and SAS to Produce Custom Rater Reliability Reports Sailesh Vezzu, Educational Testing Service, Princeton, NJ

Using PROC SQL to Generate Shift Tables More Efficiently

Microsoft Office Illustrated Introductory, Building and Using Queries

WKn Chapter. Note to UNIX and OS/390 Users. Import/Export Facility CHAPTER 9

Ten Great Reasons to Learn SAS Software's SQL Procedure

Using PROC SQL to Calculate FIRSTOBS David C. Tabano, Kaiser Permanente, Denver, CO

Why Is This Subject Important? You Could Look It Up: An Introduction to SASHELP Dictionary Views. What Information is Listed in Dictionary Tables?

SAS File Management. Improving Performance CHAPTER 37

A Breeze through SAS options to Enter a Zero-filled row Kajal Tahiliani, ICON Clinical Research, Warrington, PA

It s Proc Tabulate Jim, but not as we know it!

QC Your RDBMS Data Using Dictionary Tables. Harry Droogendyk Stratia Consulting Inc.

Chapter 6: Modifying and Combining Data Sets

Statistics, Data Analysis & Econometrics

Ditch the Data Memo: Using Macro Variables and Outer Union Corresponding in PROC SQL to Create Data Set Summary Tables Andrea Shane MDRC, Oakland, CA

ABSTRACT. Paper CC-031

The REPORT Procedure CHAPTER 32

Getting it Done with PROC TABULATE

KEPT IN TRANSLATION: AVOIDING DATA LOSS AND OTHER PROBLEMS WHEN CONVERTING JAPANESE DATA

ODS for PRINT, REPORT and TABULATE

Getting Up to Speed with PROC REPORT Kimberly LeBouton, K.J.L. Computing, Rossmoor, CA

ET01. LIBNAME libref <engine-name> <physical-file-name> <libname-options>; <SAS Code> LIBNAME libref CLEAR;

Cleaning up your SAS log: Note Messages

Using Data Set Options in PROC SQL Kenneth W. Borowiak Howard M. Proskin & Associates, Inc., Rochester, NY

The Proc Transpose Cookbook

Get SAS sy with PROC SQL Amie Bissonett, Pharmanet/i3, Minneapolis, MN

SAS Programming Techniques for Manipulating Metadata on the Database Level Chris Speck, PAREXEL International, Durham, NC

Create a Format from a SAS Data Set Ruth Marisol Rivera, i3 Statprobe, Mexico City, Mexico

An Introduction to PROC REPORT

Transcription:

Paper 11863-2016 Uncommon Techniques for Common Variables Christopher J. Bost, MDRC, New York, NY ABSTRACT If a variable occurs in more than one data set being merged, the last value (from the variable in the rightmost data set listed on the MERGE statement) is kept. It is critical, therefore, to know the name and sources of any common variables. This paper reviews simple techniques to note overwriting in the log, to summarize the names and sources of all variables, and to identify common variables before merging files. INTRODUCTION It is simple to merge SAS data sets. A DATA step with a MERGE statement and a BY statement is used to perform a match-merge. The only requirement is that observations in all input data sets are sorted with PROC SORT, indexed, or already in sort order by values of the BY variable(s). If the data sets have common variables (other than the BY variable[s]), values are overwritten. It is better to know if there are common variables and pre-process data as needed. The following sections describe how to use options, PROC FREQ, and PROC SQL to identify common variables before files are merged. CHECKING FOR COMMON VARIABLES WITH OPTIONS The option MSGLEVEL=I can be used to include information in the log when variables will be overwritten. Consider the following example that merges three SAS data sets: DS1, DS2, and DS3. DS1 contains variables id, a, b, and c. DS2 contains variables ID, B, C, and D. DS3 contains variables id, d, e, and f. The three data sets can be sorted by ID and merged. Variables b and c in data set DS1 will be overwritten by variables B and C in data set DS2. Variable D in data set DS2 will be overwritten by variable d in data set DS3: proc sort data=ds1; by id; proc sort data=ds2; by id; proc sort data=ds3; by id; options msglevel=i; data ds123; merge ds1 ds2 ds3; by id; The following is included in the log: INFO: The variable b on data set WORK.DS1 will be overwritten by data set WORK.DS2. INFO: The variable c on data set WORK.DS1 will be overwritten by data set WORK.DS2. INFO: The variable D on data set WORK.DS2 will be overwritten by data set WORK.DS3. Output 1. Information included in the log by MSGLEVEL=I. 1

This details the variables that will be overwritten. If it was intended, this information is documentation. If it was not intended, this information documents a problem. If the resulting data set is not what was needed, these data sets were also sorted and merged unnecessarily. This could be costly with larger data sets. It is possible to check if any variables will be overwritten without sorting and without merging observations. For example: options msglevel=i obs=0; data _null_; merge ds1 ds2 ds3; by id; options msglevel=n obs=max; The OPTIONS statement sets MSGLEVEL=I for information about any variables that will be overwritten. It also sets OBS=0 to read and write 0 observations. The DATA _NULL_; statement tells SAS to run a DATA step but not create a data set. The MERGE statement specifies data sets DS1, DS2, and DS3. The BY statement specifies to merge observations by ID. The RUN; statement ends the step. The second OPTIONS statement sets MSGLEVEL=N to print only the usual errors, warnings, and notes. It also sets OBS=MAX to restore reading and writing all observations. The information included in the log is the same as Output 1: INFO: The variable b on data set WORK.DS1 will be overwritten by data set WORK.DS2. INFO: The variable c on data set WORK.DS1 will be overwritten by data set WORK.DS2. INFO: The variable D on data set WORK.DS2 will be overwritten by data set WORK.DS3. Output 2. Information included in the log by MSGLEVEL=I. Pros of this technique: It is fast and efficient. Only descriptor information is combined. Any variable that will be overwritten is documented in the log. There are no real cons to this technique. It is advisable to use MSGLEVEL=I when merging data sets. CHECKING FOR COMMON VARIABLES WITH PROC FREQ The view SASHELP.VCOLUMN can be used to identify common variables. It retrieves information about variables in all SAS data sets in currently defined libraries. Run PROC CONTENTS on the view to display its structure: proc contents data=sashelp.vcolumn; The results are: Data Set Name SASHELP.VCOLUMN Observations. Member Type VIEW Variables 18 Engine SQLVIEW Indexes 0 Created 06/25/2015 01:24:08 Observation Length 523 Last Modified 06/25/2015 01:24:08 Deleted Observations 0 Protection Compressed NO Data Set Type Sorted NO Label Data Representation Default Encoding Default Output 3a. PROC CONTENTS of SASHELP.VCOLUMN. 2

Alphabetic List of Variables and Attributes # Variable Type Len Flags Label 10 format Char 49 P-- Column Format 12 idxusage Char 9 P-- Column Index Type 11 informat Char 49 P-- Column Informat 9 label Char 256 P-- Column Label 6 length Num 8 P-- Column Length 1 libname Char 8 P-- Library Name 2 memname Char 32 P-- Member Name 3 memtype Char 8 P-- Member Type 4 name Char 32 P-- Column Name 15 notnull Char 3 P-- Not NULL? 7 npos Num 8 P-- Column Position 16 precision Num 8 P-- Precision 17 scale Num 8 P-- Scale 13 sortedby Num 8 P-- Order in Key Sequence 18 transcode Char 3 P-- Transcoded? 5 type Char 4 P-- Column Type 8 varnum Num 8 P-- Column Number in Table 14 xtype Char 12 P-- Extended Type Output 3b. PROC CONTENTS of SASHELP.VCOLUMN (continued). Run PROC PRINT on SASHELP.VCOLUMN and include select variables and observations for inspection: proc print data=sashelp.vcolumn; var libname memname name; where libname='work' and memname in ('DS1','DS2','DS3'); The PROC PRINT statement starts the procedure. The VAR statement specifies to print LIBNAME (libref), MEMNAME (data set name), and NAME (variable). The WHERE statement limits observations to those for variables in data sets DS1, DS2, and DS3 in the WORK library. Note that values of LIBNAME and MEMNAME are stored in uppercase. The results are: Obs libname memname name 1 WORK DS1 id 2 WORK DS1 a 3 WORK DS1 b 4 WORK DS1 c 5 WORK DS2 ID 6 WORK DS2 B 7 WORK DS2 C 8 WORK DS2 D 9 WORK DS3 id 10 WORK DS3 d 11 WORK DS3 e 12 WORK DS3 f Output 4. PROC PRINT of select variables and observations in SASHELP.VCOLUMN. Note that there is one observation per variable in each data set and that variable names are in mixed case. Document the names and sources of all variables with a crosstab of NAME and MEMNAME: proc freq data=sashelp.vcolumn order=formatted; tables name*memname/nopercent norow nocol; format name $upcase.; where libname='work' and memname in ('DS1','DS2','DS3'); 3

The PROC FREQ statement starts the procedure. ORDER=FORMATTED orders results by the formatted values of variables. The TABLES statement specifies a crosstab of NAME (variable name) and MEMNAME (data set name). The NOPERCENT, NOROW, and NOCOL options after the slash suppress printing any percentages. The FORMAT statement associates the $UPCASE. format with NAME to treat values as uppercase. The WHERE statement limits observations to those for variables in data sets DS1, DS2, and DS3 in the WORK library. The results are: The FREQ Procedure Table of name by memname name(column Name) memname(member Name) Frequency DS1 DS2 DS3 Total A 1 0 0 1 B 1 1 0 2 C 1 1 0 2 D 0 1 1 2 E 0 0 1 1 F 0 0 1 1 ID 1 1 1 3 Total 4 4 4 12 Output 5. PROC FREQ crosstab of NAME and MEMNAME in SASHELP.VCOLUMN. In each table cell, 1 indicates a variable is in a data set and 0 indicates a variable is not in a data set. If the value of Total for any row is greater than 1, it means that variable is in more than one data set. Pros of this technique: It is metadata driven. It uses a single PROC FREQ step. It documents variables in any number of data sets. There are no real cons to this technique, although it can produce a lot of output when there is a large number of variables. CHECKING FOR COMMON VARIABLES WITH PROC SQL The PROC SQL DICTIONARY table DICTIONARY.COLUMNS can be used to identify common variables. It has information about variables in all SAS data sets in currently defined libraries. Use DESCRIBE TABLE to display its structure: proc sql; describe table dictionary.columns; quit; Results are printed in the log: 4

40 describe table dictionary.columns; NOTE: SQL table DICTIONARY.COLUMNS was created like: create table DICTIONARY.COLUMNS ( libname char(8) label='library Name', memname char(32) label='member Name', memtype char(8) label='member Type', name char(32) label='column Name', type char(4) label='column Type', length num label='column Length', npos num label='column Position', varnum num label='column Number in Table', label char(256) label='column Label', format char(49) label='column Format', informat char(49) label='column Informat', idxusage char(9) label='column Index Type', sortedby num label='order in Key Sequence', xtype char(12) label='extended Type', notnull char(3) label='not NULL?', precision num label='precision', scale num label='scale', transcode char(3) label='transcoded?' ); 41 quit; Output 6. DESCRIBE TABLE output for DICTIONARY.COLUMNS. Run a query on DICTIONARY.COLUMNS and select columns and rows for inspection: proc sql; select libname,memname,name from dictionary.columns where libname='work' and memname in ('DS1','DS2','DS3'); quit; The PROC SQL statement starts the procedure. The SELECT clause selects columns LIBNAME (libref), MEMNAME (data set name), and NAME (variable). The FROM clause reads rows from DICTIONARY.COLUMNS. The WHERE clause limits rows to those for columns in tables DS1, DS2, and DS3 in the WORK library. Note that values of LIBNAME and MEMNAME are stored in uppercase. The results are: Library Member Column Name Name Name -------------------------------- WORK DS1 id WORK DS1 a WORK DS1 b WORK DS1 c WORK DS2 ID WORK DS2 B WORK DS2 C WORK DS2 D WORK DS3 id WORK DS3 d WORK DS3 e WORK DS3 f Output 7. PROC SQL query of select columns and rows in DICTIONARY.COLUMNS. 5

Note that there is one row per column in each table and that column names are in mixed case. Run a query that selects LIBNAME (libref), MEMNAME (data set name), and NAME (variable) when NAME occurs more than once: proc sql; select libname,memname,upcase(name) as name from dictionary.columns where libname='work' and memname in ('DS1','DS2','DS3') group by upcase(name) having count(*) > 1 order by name,memname; quit; The SELECT clause converts values of NAME to uppercase. The GROUP BY clause groups rows by uppercase values of NAME. The HAVING clause post-processes each group and includes it (i.e., all rows in that group) in results when there is more than one row (i.e., the variable occurs in more than one table). The ORDER BY clause sorts results by NAME and MEMNAME. The results are: Library Member Name Name name ---------------------------------------------------- WORK DS1 B WORK DS2 B WORK DS1 C WORK DS2 C WORK DS2 D WORK DS3 D WORK DS1 ID WORK DS2 ID WORK DS3 ID Output 8. PROC SQL query of NAME values that occur more than once. Variables B, C, D, and ID occur in more than one data set. Variables B and C occur in DS1 and DS2. Variable D occurs in DS2 and DS3. Variable ID occurs in DS1, DS2, and DS3. Query results can be saved in a SAS data set. Document the names and sources of all variables that occur in more than one data set with a crosstab of NAME and MEMNAME: proc sql; create table common as select libname,memname,upcase(name) as name from dictionary.columns where libname='work' and memname in ('DS1','DS2','DS3') group by upcase(name) having count(*) > 1; quit; proc freq data=common; tables name*memname/nopercent norow nocol; The CREATE TABLE clause saves query results in data set COMMON. The PROC FREQ step processes data set COMMON. 6

The results are: The FREQ Procedure Table of name by memname name memname(member Name) Frequency DS1 DS2 DS3 Total B 1 1 0 2 C 1 1 0 2 D 0 1 1 2 ID 1 1 1 3 Total 3 4 2 9 Output 9. PROC FREQ crosstab of NAME and MEMNAME (common variables only). This crosstab documents the names and sources of variables that occur in more than one data set. Pros of this technique: It documents only problematic (common) variables and sources. Data can be pre-processed as desired (e.g., drop the ID variable). The are no real cons to this technique, but it requires PROC SQL syntax that might be new to some. CONCLUSION Inspect data sets for common variables before merging. Use MSGLEVEL=I to note variables that will be overwritten in the log. Use PROC FREQ and SASHELP.VCOLUMN to summarize variables and sources. Use PROC SQL, DICTIONARY.COLUMNS, and PROC FREQ to summarize variables that occur more than once. Metadata about variables and sources can be summarized with PROC TABULATE and PROC REPORT. See the appendix for additional examples. CONTACT INFORMATION Your comments and questions are valued and encouraged. Contact the author at: Christopher J. Bost MDRC 16 East 34 th Street New York, NY 10016 (212) 340-8613 christopher.bost@mdrc.org chrisbost@gmail.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

APPENDIX Metadata about variables and sources can be summarized with more than one procedure. Results can be formatted different ways. Consider the following examples. PROC FREQ WITH RENAMED VARIABLES NAME and MEMNAME can be renamed to something more intuitive (e.g., Variable and Dataset) in the crosstab. Variable labels can be suppressed. Results can be output to Rich Text Format: proc freq data=sashelp.vcolumn(rename=(name=variable memname=dataset)) order=formatted; tables variable*dataset/nopercent norow nocol; format variable $upcase.; attrib _all_ label=' '; where libname='work' and dataset in ('DS1','DS2','DS3'); The results are: Table of Variable by Dataset Variable Dataset Frequency DS1 DS2 DS3 Total A 1 0 0 1 B 1 1 0 2 C 1 1 0 2 D 0 1 1 2 E 0 0 1 1 F 0 0 1 1 ID 1 1 1 3 Total 4 4 4 12 Output A1. PROC FREQ crosstab with renamed variables. PROC TABULATE WITH A SYMBOL The 1s and 0s under DS1, DS2, and DS3 in Output A1 are frequencies. PROC TABULATE can count frequencies and format the results with a special character: ods escapechar='^'; proc format; value bullet.=' ' 1='^{unicode 25AA}'; *25AA is Unicode for Black Small Square; proc tabulate data=sashelp.vcolumn order=formatted style=[just=center]; class name memname; format name $upcase.; table name=' ', memname=' '*n=' '*format=bullet. /box='variable'; where libname='work' and memname in ('DS1','DS2','DS3'); 8

ODS ESCAPECHAR= specifies the inline formatting symbol used to enable special formatting. The PROC FORMAT statement starts the procedure. The VALUE statement: - Creates a temporary numeric format named BULLET. - Labels missing values with a blank space (i.e., nothing). - Labels values of 1 with the Unicode character 25AA (). Specify a Unicode character starting with the previously assigned ODS ESCAPECHAR, a left brace, the word UNICODE, a space, the value for the respective character, and a right brace. Under Windows, click the Start button and search for character map to see all of the symbols that can be displayed. Click on any symbol to display its Unicode value at the bottom. Alternately, use a search engine to find lists of Unicode characters and values. The RUN; statement ends the FORMAT procedure. The PROC TABULATE statement starts the procedure. ORDER=FORMATTED orders results by the formatted values of variables. STYLE=[JUST=CENTER] centers the results in table cells. The CLASS statement specifies using values of NAME and MEMNAME to group data. The FORMAT statement associates the $UPCASE. format with NAME to treat values as uppercase. The TABLE statement: - Specifies NAME in the row dimension. - Specifies MEMNAME*N in the column dimension. - Formats values of N with the BULLET. format. - Uses = after NAME, MEMNAME, and N to suppress column headers. - Uses /BOX= to print Variable in the upper left box of the table. (It is blank by default.) The WHERE statement limits observations to those for variables in data sets DS1, DS2, and DS3 in the WORK library. Note that values of LIBNAME and MEMNAME are stored in uppercase. The RUN; statement ends the TABULATE procedure. The results are: Variable DS1 DS2 DS3 A B C D E F ID Output A2. PROC TABULATE table with symbol. PROC REPORT WITH TRAFFIC LIGHTING The symbols in Output A2 help make an effective visual. It is necessary to scan each row, however, to see which variable occurs in more than one table. PROC REPORT can count frequencies, format the results with a special character, and highlight rows that meet specified criteria: 9

ods escapechar='^'; proc format; value bullet.=' ' 1='^{unicode 25AA}'; *25AA is Unicode for Black Small Square; proc report data=sashelp.vcolumn; where libname='work' and memname in ('DS1','DS2','DS3'); column name memname,n total; define name/group ' ' format=$upcase. order=formatted style=[font_weight=bold]; define memname/across ' '; define n/' ' format=bullet. center; define total/computed noprint; compute total; if sum(_c2_,_c3_,_c4_)>1 then do; call define(_row_,'style','style=[background=lightyellow]'); end; endcomp; ODS ESCAPECHAR= and the PROC FORMAT step are the same as in the PROC TABULATE example. The PROC REPORT statement starts the procedure. The WHERE statement limits observations to those for variables in data sets DS1, DS2, and DS3 in the WORK library. Note that values of LIBNAME and MEMNAME are stored in uppercase. The COLUMN statement specifies the arrangement of columns in the report. The DEFINE statements specify how to use and display report items: - NAME is a group variable. Uppercase values will be in a bold font (like column headings). - MEMNAME is an across variable. Each of its values will get its own column in the report. - N is a statistic to calculate. Values will be formatted with the BULLET. format and centered. - TOTAL is a computed variable. Its values will be calculated, but not printed, in the report. - Note that all column headers are suppressed by labeling the variables with a blank ( ). The COMPUTE statement begins a compute block: - It contains programming statements that PROC REPORT will execute as it builds the report. - IF the sum of columns 2, 3, and 4 in a row (i.e., values for DS1, DS2, and DS3) is greater than 1 (i.e., a variable occurs in more than one data set), THEN style the row with a light yellow background. - The ENDCOMP; statement ends the compute block. The results are: A DS1 DS2 DS3 B C D E F ID Output A3. PROC REPORT table with traffic lighting. 10