Using SAS/SCL to Create Flexible Programs... A Super-Sized Macro Ellen Michaliszyn, College of American Pathologists, Northfield, IL

Similar documents
Going Under the Hood: How Does the Macro Processor Really Work?

Unlock SAS Code Automation with the Power of Macros

A Quick and Gentle Introduction to PROC SQL

Tweaking your tables: Suppressing superfluous subtotals in PROC TABULATE

PharmaSUG Paper TT11

The Proc Transpose Cookbook

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

Creating Macro Calls using Proc Freq

The REPORT Procedure: A Primer for the Compute Block

%Addval: A SAS Macro Which Completes the Cartesian Product of Dataset Observations for All Values of a Selected Set of Variables

Seminar Series: CTSI Presents

A Side of Hash for You To Dig Into

One SAS To Rule Them All

Virtual Accessing of a SAS Data Set Using OPEN, FETCH, and CLOSE Functions with %SYSFUNC and %DO Loops

%MAKE_IT_COUNT: An Example Macro for Dynamic Table Programming Britney Gilbert, Juniper Tree Consulting, Porter, Oklahoma

KEYWORDS Metadata, macro language, CALL EXECUTE, %NRSTR, %TSLIT

Sorting and Filtering Data

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

Document and Enhance Your SAS Code, Data Sets, and Catalogs with SAS Functions, Macros, and SAS Metadata. Louise S. Hadden. Abt Associates Inc.

While You Were Sleeping, SAS Was Hard At Work Andrea Wainwright-Zimmerman, Capital One Financial, Inc., Richmond, VA

A Generic Solution to Running the SAS System on the Web Without SAS/Intrnet David L. Ward, InterNext, Inc., Somerset, NJ

Exsys RuleBook Selector Tutorial. Copyright 2004 EXSYS Inc. All right reserved. Printed in the United States of America.

Data Manipulation with SQL Mara Werner, HHS/OIG, Chicago, IL

I KNOW HOW TO PROGRAM IN SAS HOW DO I NAVIGATE SAS ENTERPRISE GUIDE?

Why Hash? Glen Becker, USAA

Chapter 6: Modifying and Combining Data Sets

The Dataset Diet How to transform short and fat into long and thin

The Ins and Outs of %IF

Introduction. Getting Started with the Macro Facility CHAPTER 1

What's the Difference? Using the PROC COMPARE to find out.

Data Quality Review for Missing Values and Outliers

HOW TO DEVELOP A SAS/AF APPLICATION

Checking for Duplicates Wendi L. Wright

Table of Contents. How to use this document. How to use the template. Page 1 of 9

Top Coding Tips. Neil Merchant Technical Specialist - SAS

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

CHAPTER 7 Using Other SAS Software Products

SAS Data Integration Studio Take Control with Conditional & Looping Transformations

ABSTRACT INTRODUCTION MACRO. Paper RF

Macro Magic III. SNUG Presentation (30mins)

Dstewart.com FAQs Reference Guide

HCA Tech Note. Port Forwarding

Macros for Two-Sample Hypothesis Tests Jinson J. Erinjeri, D.K. Shifflet and Associates Ltd., McLean, VA

PharmaSUG China. Systematically Reordering Axis Major Tick Values in SAS Graph Brian Shen, PPDI, ShangHai

Easing into Data Exploration, Reporting, and Analytics Using SAS Enterprise Guide

Lab #3: Probability, Simulations, Distributions:

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

Paper CC16. William E Benjamin Jr, Owl Computer Consultancy LLC, Phoenix, AZ

SAS Macro Programming for Beginners

Macro Architecture in Pictures Mark Tabladillo PhD, marktab Consulting, Atlanta, GA Associate Faculty, University of Phoenix

ABSTRACT INTRODUCTION TRICK 1: CHOOSE THE BEST METHOD TO CREATE MACRO VARIABLES

Using PROC SQL to Generate Shift Tables More Efficiently

Purchase this book at

The 'SKIP' Statement

Paper A Simplified and Efficient Way to Map Variable Attributes of a Clinical Data Warehouse

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

Let s Get FREQy with our Statistics: Data-Driven Approach to Determining Appropriate Test Statistic

Copy That! Using SAS to Create Directories and Duplicate Files

A SAS Solution to Create a Weekly Format Susan Bakken, Aimia, Plymouth, MN

Automate Clinical Trial Data Issue Checking and Tracking

Files Arriving at an Inconvenient Time? Let SAS Process Your Files with FILEEXIST While You Sleep

SAS Viewer giving way to Universal Viewer Steve Wright, Quintiles, RTP, NC

Purpose: This chapter demonstrates how to create pie charts, and discusses some of the issues to consider when deciding whether to use a pie chart.

Using Proc Freq for Manageable Data Summarization

Creating output datasets using SQL (Structured Query Language) only Andrii Stakhniv, Experis Clinical, Ukraine

HOW TO EXPORT BUYER NAMES & ADDRESSES FROM PAYPAL TO A CSV FILE

Ranking Between the Lines

Get Started Writing SAS Macros Luisa Hartman, Jane Liao, Merck Sharp & Dohme Corp.

Don't quote me. Almost having fun with quotes and macros. By Mathieu Gaouette

Common Sense Validation Using SAS

Top 15 Excel Tutorials

Introduction to SAS. Hsueh-Sheng Wu. Center for Family and Demographic Research. November 1, 2010

Automated Checking Of Multiple Files Kathyayini Tappeta, Percept Pharma Services, Bridgewater, NJ

A Step-by-Step Guide to Survey Success

Collector and Dealer Software - CAD 3.1

3N Validation to Validate PROC COMPARE Output

Prevent Pane - Moving from MENU & PROGRAM Entries to FRAME Entries Loretta Golby, ISM Alberta Serge Dupuis, BKD Software Consultants Edmonton, Alberta

Developing Data-Driven SAS Programs Using Proc Contents

Get into the Groove with %SYSFUNC: Generalizing SAS Macros with Conditionally Executed Code

Useful Tips When Deploying SAS Code in a Production Environment

Using GSUBMIT command to customize the interface in SAS Xin Wang, Fountain Medical Technology Co., ltd, Nanjing, China

David S. Septoff Fidia Pharmaceutical Corporation

Using Templates Created by the SAS/STAT Procedures

Arthur L. Carpenter California Occidental Consultants, Oceanside, California

An Introduction to Macros Deb Cassidy

Internet/Intranet, the Web & SAS

SESUG 2014 IT-82 SAS-Enterprise Guide for Institutional Research and Other Data Scientists Claudia W. McCann, East Carolina University.

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

This chapter is intended to take you through the basic steps of using the Visual Basic

Getting Classy: A SAS Macro for CLASS Statement Automation

A Macro that can Search and Replace String in your SAS Programs

Journey to the center of the earth Deep understanding of SAS language processing mechanism Di Chen, SAS Beijing R&D, Beijing, China

Multiple Graphical and Tabular Reports on One Page, Multiple Ways to Do It Niraj J Pandya, CT, USA

Jennifer Clegg and Carol Rigsbee, SAS Institute Inc., Cary, NC

Depiction of program declaring a variable and then assigning it a value

Introduction to the SAS System

SAS Macro Language: Reference

Functions vs. Macros: A Comparison and Summary

Paper S Data Presentation 101: An Analyst s Perspective

Paper HOW-06. Tricia Aanderud, And Data Inc, Raleigh, NC

Transcription:

Using SAS/SCL to Create Flexible Programs... A Super-Sized Macro Ellen Michaliszyn, College of American Pathologists, Northfield, IL ABSTRACT SAS is a powerful programming language. When you find yourself writing repetitive code, you should use a macro. You probably already know that, and might even do it sometimes. There are limitations to what a macro can do, and that can get frustrating. It may even be a deterrent to trying. The big problems come when you need a parameter that is unknown at compile time... or when you spend almost as much time writing the macro calls as you would just writing the whole thing "longhand." With SCL you are able to write code that is both efficient and stable. Once the code has been tested, it will not need to be edited if the parameters change. Hard coding of macro calls means there is potential for errors, which means that testing would be necessary after every macro call change. When there are lots of macro calls with multiple parameters, these updates can be time consuming, and it is easy to make mistakes. When the parameters that are entered manually could be found programmatically based on the data, there has to be a better way. INTRODUCTION Using SCL, you can dynamically create Base SAS code based on parameters either calculated or read in from a dataset. You can create Base SAS code from SCL by using submit statements to put the code in a preview buffer. The code that goes in the preview buffer can change depending on the values of the SCL variables and other data. Anything that you can write in Base SAS can be run in SCL. Base SAS code enclosed in a submit block will stay in a preview buffer until you are ready to run it, similar to a macro. This means that you can build your code, line by line if necessary, based on whatever SCL variables, loops, and if-statements you want to use. SCL variables can be read from datasets, kept in a list, read from a SAS/AF interface or assigned conditionally. Unlike a macro, the values of the parameters do not have to be known at compile time. I repeat... The values of the parameters DO NOT have to be known at compile time. This is a really big deal. It means values of parameters can change based on data, loops, conditions, other datasets, data read in from external sources, calculated randomly, pretty much anything you want. An SCL program is amazingly flexible. For example, you can use SCL to write the first part of your base SAS code, leave it in the preview buffer for later, use SCL code to read in data, use that data to create more code for the preview buffer, perform some calculations in SCL, use the calculated value in a loop to add code to the preview buffer, then use SCL to put the closing statements of the base SAS code in the preview buffer and run it. When that is done, you can continue the SCL code, even reading in your newly created dataset(s) to create more base SAS code if you choose. With this method of programming, the SCL code can be compiled and saved for later use. (NOTE: an SCL license is NOT required to run the compiled code.) 1

IMPLEMENTATION SCL code can contain submit blocks. These start with submit and end with endsubmit. For the final submit block, you will use submit continue, and the matching endsubmit will prompt the code in the preview buffer to be run. SCL variables can be used in a very similar way to macro variables. Inside submit blocks, the SCL variables are referenced the same way (&varname). One small difference is that SCL vars will be resolved inside single quotes, not just double quotes. Below are some examples of how to take advantage of the power of SCL. EXAMPLE 1 This example includes three versions of the same program. Section 1 uses a simple macro with parameters passed to it, Section 2 uses SCL code with parameters hard coded, and Section 3 uses SCL code to read a dataset that provides the parameters. The program itself is simple. It sorts the data and creates a report of the specified summary variables and by variables. In this example, there are two reports created. Note: This code can be run as it is written below, because it uses the sample datasets in the sashelp library. Section 1: Macro Version %macro SortAndSummary(dset,byvars,summaryVar); /* Sort the data by specified variable(s) */ proc sort data=&dset out=tempdset; by &byvars; /* Run a procedure with parameters specified in macro */ proc means data=tempdset; class &byvars; var &summaryvar; %mend; /* Report 1 */ %SortAndSummary(dset=sashelp.shoes,byvars=region subsidiary, summaryvar=sales); /* Report 2 */ %SortAndSummary(dset=sashelp.shoes,byvars=product,summaryVar=sales); /* Repeat for more reports, if necessary */ 2

Section 2: SCL Code Version In this section, the macro code is modified to use SCL exactly like the macro above. It still needs to be modified whenever the parameters change. Notice the section SORTANDSUMMARY that contains the code that was inside the macro SortAndSummary. The section GETPARMS assigns each of the parameters to an SCL variable, which are used in the SORTANDSUMMARY section. INIT: link getparms; GETPARMS: /* Report 1 */ /* Assign SCL variables (same as macro variables in Section 1) */ dset='sashelp.shoes'; byvars='region subsidiary'; summaryvar='sales'; link SortAndSummary; /* Report 2 */ dset='sashelp.shoes'; byvars='product'; summaryvar='sales'; link SortAndSummary; /* Repeat for more reports, if necessary */ SORTANDSUMMARY: /* Here is the code that parallels the macro */ /* It is called using a link statement */ submit continue; /* Sort the data by specified variable(s) */ proc sort data=&dset out=tempdset; by &byvars; /* Run a procedure with parameters specified in macro */ proc means data=tempdset; class &byvars; var &summaryvar; 3

Section 3: SCL Code Using Parameters from a Dataset This section reads the input parameters from a dataset and runs the reports. With this code, all edits would be done to the control dataset, and the code can be tested, compiled, and would not need to be modified if the report was needed on an additional dataset and/or variables. A preliminary step could be added to read external data and use that data to determine the parameters. See Appendix: Example 1 for the code to create the controlparms dataset. It will look like the following: dset byvars summaryvar sashelp.shoes region subsidiary sales sashelp.shoes product sales Below is the SCL code modified to read the parameters from the dataset. The SORTANDSUMMARY section has not been changed, and the GETPARMS section now reads in the data. INIT: link getparms; GETPARMS: /* Read parameters from control dataset */ /* Open the control dataset */ dsid=open('controlparms','i'); /* Read the first row */ rc=fetch(dsid); /* Repeat for every row read from the dataset */ do until(rc=0); dset=getvarc(dsid,1); byvar=getvarc(dsid,2); summaryvar=getvarc(dsid,3); link SortAndSummary; /* Read the next row of control dataset */ rc=fetch(dsid); end; /* Close the control dataset */ if dsid then rc=close(dsid); SORTANDSUMMARY: submit continue; proc sort data=&dset out=tempdset; by &byvar; proc means data=tempdset; class &byvar; var &summaryvar; 4

The benefits of doing it this way is that it can be controlled with a dataset and the code does not need to be changed if the same report with different data is requested. This method can be used for anything that a macro can do, and many things that a macro cannot. The SAS/AF interface could also be used to give the user control, either by typing the parameters or selecting from a list at run time. EXAMPLE 2: Here we start with data that contains a count of items for an ID in one long row. The output dataset should contain one row per item. Many times data comes from other databases or spreadsheets (especially from nonprogrammers) that are not organized efficiently. If you look at the data, there is often a repeating pattern. One example would be buyer, price, ID, date, price, ID, date, price, ID, date... In this example, we have a row for each buyer. That row includes the buyer name, and for each item it includes how many of each item were purchased at what price. Below is the input data. All variables from V010 to V150 and P010 to P150 exist (counting by 10s). For those of you following along, the code to create this sample input dataset is in the appendix. Buyer N010 P010 N020 P020 N030... P150 Phone Name 1 16 5.99 20 15.49 13... 23.99 4135551212 Name 2 17 6.99 21 16.49 14... 24.99 7085551212 The final data should include one row per ID for every buyer. Each row will have the buyer name, item ID, count, price, and total cost for that item. Below is the requested output data. The value of ID matches the numeric portion of the variable name. Buyer ID Count Price Total Name 1 10 16 5.99 95.84 Name 1 20 20 15.49 309.8............... Name 2 150 21 24.99 524.79 5

INIT: /* This program builds the Base SAS code to rearrange the dataset */ /* Put the beginning of the data step in the preview buffer */ submit; data outputds(keep=buyer ID count price total); set inputds(keep=buyer /* Don t close the parenthesis so more variables can be added below */ /* Add each variable to the keep statement */ /* list vars that are multiples of 10 */ numvars=0; do i=10 to 150 by 10; varnamen='n' put(i,z3.); /* format z3. includes leading zeros */ varnamep='p' put(i,z3.); numvars+1; /* for each i, put the varnames in the preview buffer */ submit; &varnamen &varnamep end; /* Add close parenthesis for keep statement to the preview buffer */ submit; ); /* Continue the data step and run the code in the preview buffer at the next endsubmit */ submit continue; /* Assign the arrays so they contain all of the N* or P* variables */ array allvarsn {*} N: ; array allvarsp {*} P: ; /* For each, assign ID, count and price, calculate total, and output */ do j=1 to dim(allvarsn); ID=j*10; count=allvarsn[j]; price=allvarsp[j]; /* Calculate Total */ total=count*price; if count ne '' then output; end; 6

When the SCL code (Section 3, above) is executed, the SAS Log will contain the code that was in the preview buffer. The first part of the log is included below. Notice the keep statement. The variable names were created using SCL and each pair was added inside the do-loop. data outputds(keep=buyer ID count price total); set inputds(keep=buyer /* Don t close the parenthesis so more variables can be added below */ N010 P010 N020 P020 N030 P030... N140 P140 N150 P150 ); /* Assign the arrays so they contain all of the N* or P* variables */ array allvarsn {*} N:;... EXAMPLE 3: The dataset mergeme contains a list of datasets that need to be merged by byvars. This example reads the parameters from mergeme and adds the value from each observation to the SAS preview buffer. dsetname maps.chile maps.chile2 byvars ID ID /* Merge all datasets found in the dataset mergeme */ INIT: link runmerge; RUNMERGE: submit; /* Put beginning of data step into the preview buffer */ data mergedds; merge /* Open the dataset and fetch the first observation */ dsid=open('mergeme','i'); if dsid ne 0 then do; rc=fetch(dsid); 7

/* Loop through each observation in the dataset */ do until(rc ne 0); dset=getvarc(dsid,varnum(dsid,'dsetname')); byvars=getvarc(dsid,varnum(dsid,'byvars')); submit; /* Add dataset name to preview buffer */ &dset rc=fetch(dsid); end; submit continue; /* Close data step and run code in preview buffer */ ; by &byvars; end; TERM: /* Close the mergeme dataset */ if dsid then rc=close(dsid); The code from the preview buffer from this example is below. The important part of this is that the list of datasets is external. /* Add dataset name to preview buffer */ data mergedds; merge /* Add dataset name to preview buffer */ maps.chile /* Add dataset name to preview buffer */ maps.chile2 /* Close data step and run code in preview buffer */ ; by ID; 8

EXAMPLE 4: In this example, labels are assigned for a long row of repetitive data that has generic variable names, and a report is run for each grouping (Min, Max, Mean and SD). This is a fairly typical example of data that is created and updated by someone (not a programmer, of course) in Excel. If a car make or model is added or deleted, there could be a big problem with the code. Everything would need to be renumbered if there was a make added or deleted, and the number of models (car N) would need to be manually re-checked every time. Section 3, which is the SCL version, reads in the information from another dataset and creates the code using that data. After one check of that dataset, the code would be generated perfectly. The input dataset transdata contains the min, max, mean, and SD for each car make (Acura, Audi, BMW,, Volvo). COL1 is Acura Min, COL2 is Acura Max, COL3 is Acura Mean, COL4 is Acura SD, COL5 is Audi Min, COL6 is Audi Max,, COL152 is Volvo SD. Dataset transdata This input dataset contains summary price data for each car make. TYPE COL1 COL2 COL3 COL4... COL151 COL152 MSRP 23820 89765 42938.5 22189.0... 36314.2 6176.6 Invoice 21761 79978 38590.8 19550.8... 34216.2 5804.4 Dataset cartypes The input dataset cartypes contains a list of car makes and number of car models for each, in the same order as the transdata dataset. Make COUNT Acura 7 Audi 19 BMW 20...... Volvo 12 The output for each make should be printed as follows: N=7 Acura Min Acura Max Acura Mean Acura SD MSRP $23,820 $89,765 $42,939 $22,189 Invoice $21,761 $79,978 $38,591 $19,551 The code to create the input datasets for this example is in the Appendix. 9

Section 1 - Base SAS Version /* Create Labels for Reporting */ data outputdata2; set transdata; label col1='acura Min' col2='acura Max' col3='acura Mean' col4='acura SD' col5='audi Min' /* LOTS more code here - 125 lines total */ ; proc print data=outputdata2 noobs label; label type="n=7"; var type col1-col4; proc print data=outputdata2 noobs label; label type="n=19"; var type col5-col8; /* Repeat for every make - 38 total */ Section 2 - Macro Version: %macro labelset(make,startnum); %let varnum2=%eval(&startnum+1); %let varnum3=%eval(&startnum+2); %let varnum4=%eval(&startnum+3); col&startnum="&make Min" col&varnum2="&make Max" col&varnum3="&make Mean" col&varnum4="&make SD" %mend; data outputdata2; set transdata; label %labelset(make=acura,startnum=1) %labelset(make=audi,startnum=5) %labelset(make=bmw,startnum=9) /* Repeat for each make - 38 lines total */ %labelset(make=volvo,startnum=149) ; 10

%macro printit(startnum,carn); %let lastnum=%eval(&startnum+3); proc print data=outputdata2 noobs label; label type="n=&carn"; var type col&startnum-col&lastnum; %mend; %printit(startnum=1,carn=7); %printit(startnum=5,carn=19); %printit(startnum=9,carn=20); /* Repeat for each make - 38 lines total */ %printit(startnum=149,carn=12); Section 3 - SCL Version In this example, the code written to the preview buffer is based on the cartypes dataset, so the SCL code does not need to be updated even if data is added or deleted or the number of rows in cartypes and number of variables in transdata change. INIT: link createds; link printreport; CREATEDS: /* Open the cartypes dataset read only */ cartypeds=open('cartypes','i'); submit; data outputdata2; set transdata; label type='type' /* Assign labels */ rc=fetch(cartypeds); rownum=0; /* for each row in the cartypes dataset, assign the 4 labels */ do until(rc ne 0); carmake=getvarc(cartypeds,1); rownum+1; varnum1=rownum*4-3; varnum2=rownum*4-2; varnum3=rownum*4-1; varnum4=rownum*4; 11

submit; col&varnum1="&carmake Min" col&varnum2="&carmake Max" col&varnum3="&carmake Mean" col&varnum4="&carmake SD" rc=fetch(cartypeds); end; /* end data statement and submit code in preview buffer */ submit continue; ; if type='_freq_' then make='n'; else if type='_type_' then delete; PRINTREPORT: /* Print each report */ /* Read dataset starting at the beginning */ rc=fetchobs(cartypeds,1); rownum=0; /* for each row in the cartypes dataset, run the requested report */ do until(rc ne 0); carn=getvarc(cartypeds,2); rownum+1; startnum=rownum*4-3; lastnum=rownum*4; submit continue; proc print data=outputdata2 noobs label; label type="n=&carn"; format col&startnum-col&lastnum dollar10.0; var type col&startnum-col&lastnum; rc=fetch(cartypeds); end; if cartypeds then rc=close(cartypeds) 12

Included below is the code that was put into the preview buffer and executed. This can be found in the SAS Log after the SCL program has been run. You can see that the SCL code created what looks quite a bit like the code in Section 1 above, where the whole code was typed out. data outputdata2; set transdata; label type='type' col1="acura Min" col2="acura Max" col3="acura Mean" col4="acura SD"... (36 rows) col149="volvo Min" col150="volvo Max" col151="volvo Mean" col152="volvo SD" ; if type in ('_FREQ_','_TYPE_') then delete; proc print data=outputdata2 noobs label; label type="n=7"; format col1-col4 dollar10.0; var type col1-col4;... (36 proc print sections) proc print data=outputdata2 noobs label; label type="n=12"; format col149-col152 dollar10.0; var type col149-col152; 13

CONCLUSION Using SCL to build your Base SAS code is much more versatile than using a macro. Because the values of the parameters do not have to be known at compile time, it means values of parameters can change based on data, loops, conditions, other datasets, data read in from external sources, SCL variables, user input... pretty much anything you want. This method of programming is extremely flexible and allows the program to be written once, tested, and used often and in many ways. Using an SCL List, a dataset, user input, or just about anything else to control the program allows a cleaner and less error-prone option than code rewrites. It brings SAS programming to the next level. REFERENCES SAS/AF Online Documentation: http://support.sas.com/documentation/onlinedoc/af/ ACKNOWLEDGEMENTS 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. CONTACT INFORMATION Your comments and questions are valued and encouraged. Please contact the author at: Ellen Michaliszyn College of American Pathologists 847-832-7194 emichal@cap.org A SAS catalog is available with all the SCL and Base SAS code in this paper. Please send me a request. It takes just a couple clicks to send it. Also, I would love to help answer any questions you have implementing this powerful way of programming. 14

APPENDIX Example 1 Section 3 Setup Below is the code to create the control dataset (run in base SAS) data controlparms; length dset byvars summaryvar $40; /* Report 1 */ dset='sashelp.shoes'; byvars='region subsidiary'; summaryvar='sales'; output; /* Report 2 */ dset='sashelp.shoes'; byvars='product'; summaryvar='sales'; output; /* Repeat for more reports, if necessary */ Example 2 Setup data inputds(drop=i); /* this creates a dataset with 2 rows of dummy data */ do i=1 to 2; Buyer='Name ' put(i,best2.); /* N010 is Count of ID 10, P010 is price of ID 10 */ N010=i+15; P010=i+ 4.99; N020=i+19; P020=i+14.49; N030=i+12; P030=i+10.49; N040=i+14; P040=i+17.99; N050=i+1; P050=i+ 1.99; N060=i+11; P060=i+12.49; N070=i+12; P070=i+19.99; N080=i+13; P080=i+12.99; N090=i+17; P090=i+10.99; N100=i+15; P100=i+15.49; N110=i+12; P110=i+15.99; N120=i+13; P120=i+13.49; N130=i+17; P130=i+22.99; N140=i+15; P140=i+18.49; N150=i+19; P150=i+22.99; if i=1 then Phone='4135551212'; else Phone='7085551212'; output; end; 15

Example 4 Setup /* Create Datasets for Example 5 (Base SAS) */ proc means data=sashelp.cars noprint; class make; var msrp invoice; output out=inputdata(where=(_type_ ne 0 and _stat_ ne 'N')); proc transpose data=inputdata out=transdata(rename=(make=type)) name=make; data transdata; set transdata; if type in ('MSRP','Invoice'); proc freq data=sashelp.cars; tables make / noprint out=cartypes(drop=percent); 16