16 Writing Macro in SAS
Learning Objectives
In this hands-on session, we will practice writing SAS macros through a concrete example.
The goal is to help you develop a systematic workflow for turning regular SAS code into reusable and flexible macro functions.By the end of this activity, you should be able to:
- Understand the logic behind SAS macro programming.
- Identify macro inputs and outputs.
- Write simple SAS macros for statistical analysis.
- Apply macros to automate repetitive tasks.
16.1 Guideline: How to Write SAS Macros
Based on experience, a reliable way to write SAS macros is to follow three steps:
Step 1: Write Regular SAS Code First
- Write SAS code that produces the desired result without using macros.
- Clearly identify:
- What the code does
- Which values may change (dataset name, variable name, parameters, etc.)
Step 2: Introduce Macro Variables Using %LET
- Replace hard-coded values with macro variables using
%LET. - This makes the code flexible and easier to generalize.
Step 3: Wrap the Code into a Macro
- Convert the
%LETvariables into macro arguments. - Place the code inside
%MACRO ... %MEND. - Remove the
%LETstatements. - Your macro now behaves like a function.
💡 This three-step strategy is also useful when writing functions in R or Python.
16.2 Practice Examples in this activity
To practice these steps, in this activity, we will:
- Write the first macro to compute the sample mean, standard deviation as well as the \(p\)-value
- Write the second macro to conduct a one-sample Z-test
Eventually, you will practice the three-step workflow twice through those two macros
16.2.1 Dataset for this exercise: Court Length Data
We reuse the court length example from previous lecture.
DATA time;
INPUT time @@;
DATALINES;
43 90 84 87 116 95 86 99 93 92
121 71 66 98 79 102 60 112 105 98
;
RUN;16.3 First SAS Macro for Reporting Mean and Standard Deviation
Step 1: Write Regular SAS Code (No Macros)
First, write SAS code that computes the sample mean, sample size, and standard deviation from a dataset.
Key: Replace ? and ?? with the dataset name and variable name.
PROC MEANS DATA=?;
VAR ??;
OUTPUT OUT=zdata
MEAN=Mean
N=n
STDDEV=Std_Dev;
RUN;At this stage:
- Do not worry about macros.
- Focus only on producing the correct output.
Step 2: Introduce Flexibility Using %LET
Next, make the code flexible by replacing hard-coded values with macro variables.
%LET D = time; /* dataset name */
%LET V = time; /* variable name */
PROC MEANS DATA=&D;
VAR &V;
OUTPUT OUT=zdata
MEAN=Mean
N=n
STDDEV=Std_Dev;
RUN;Key idea:
- %LET creates macro variables D and V
- &D controls which dataset is used
- &V controls which variable is analyzed
This allows the same code to work for any dataset and variable.
Step 3: Change everything to a MACRO syntax
Finally, wrap the code into a macro function, that is, replace %LET variables with macro arguments and place the code inside %MACRO ... %MEND.
%MACRO myFunction(input1, input2);
...
%MEND myFunction;
%myFunction(input1, input2);16.4 Second SAS Macro for creating confidence interval and p-value from a Z-test
In this section, we apply the three-step macro writing strategy again to build a SAS macro that computes:
- a \((1-\alpha)100\)% confidence interval for the population mean, and
- the Z-test statistic and p-value for testing
\[ H_0: \mu = \mu_0. \]
You should think carefully through each step before combining everything into a macro.
You may need to utilize the following SAS functions:
Code chunk 1: Dataset
DATA time;
INPUT time @@;
DATALINES;
43 90 84 87 116 95 86 99 93 92
121 71 66 98 79 102 60 112 105 98
;
RUN;Code chunk 2: Mean, standard deviation, and sample size calculation
PROC MEANS DATA=time;
VAR time;
OUTPUT OUT=zdata
MEAN=Mean
N=n
STDDEV=Std_Dev;
RUN;For this code chunk, you may verify that:
- Mean, Std_Dev, and n are correctly stored in the dataset zdata.
Code chunk 3: CI and Z-test Quantities
DATA ztest;
SET zdata;
/* Confidence level */
CL = (1 - &alpha) * 100;
/* Confidence interval */
ZLower = Mean - probit(1 - &alpha/2) * Std_Dev / sqrt(n);
ZUpper = Mean + probit(1 - &alpha/2) * Std_Dev / sqrt(n);
/* Z-test statistic */
zts = (Mean - &muzero) / (Std_Dev / sqrt(n));
RUN;This code chunk creates:
- confidence level,
- confidence interval bounds,
- Z-test statistic.
Notes:
&alphais the significance level&muzerois the hypothesized mean under \(H_0\)probit()returns the standard normal quantile
Step 3: Introduce Macro Variables for Flexibility
Now, think about what should be user-controlled inputs:
- dataset name
- variable name
- significance level \(\alpha\)
- null mean \(\mu_0\)
Example macro variables:
%let D = time;
%let V = time;
%let alpha = 0.05;
%let muzero = 90;Update your code by replacing fixed values with macro variables.
16.5 Convert Everything into a Macro
Finally, combine everything we learned from this activity into a reusable SAS macro.
Step 1
DATA TIME;
INPUT TIME @@;
DATALINES;
43 90 84 87 116 95 86 99 93 92
121 71 66 98 79 102 60 112 105 98
;
RUN;
PROC MEANS DATA=TIME;
VAR TIME;
OUTPUT OUT=ZDATA MEAN=MEAN N=N STDDEV=STD_DEV;
RUN;
DATA ZTEST;
SET ZDATA;
CL = (1 - &ALPHA) * 100;
ZLOWER = MEAN - PROBIT(1 - &ALPHA / 2) * STD_DEV / SQRT(N);
ZUPPER = MEAN + PROBIT(1 - &ALPHA / 2) * STD_DEV / SQRT(N);
ZTS = (MEAN - &MUZERO) / (STD_DEV / SQRT(N));
IF &SIDE = 'L' THEN ZPVALUE = PROBNORM(ZTS);
IF &SIDE = 'U' THEN ZPVALUE = 1 - PROBNORM(ZTS);
IF &SIDE = 'B' THEN ZPVALUE = 2 * MIN(1 - PROBNORM(ZTS), PROBNORM(ZTS));
RUN;
PROC PRINT DATA=ZTEST NOOBS;
VAR N MEAN STD_DEV ZLOWER ZUPPER ZPVALUE;
TITLE 'Z-INTERVALS AND Z-TEST';
RUN;Step 2
%LET ALPHA = 0.05; *VALUE OF ALPHA;
%LET MUZERO = 80;
%LET SIDE = 'B';
PROC MEANS DATA=TIME;
VAR TIME;
OUTPUT OUT=ZDATA MEAN=MEAN N=N STDDEV=STD_DEV;
RUN;
DATA ZTEST;
SET ZDATA;
CL = (1 - &ALPHA) * 100;
ZLOWER = MEAN - PROBIT(1 - &ALPHA / 2) * STD_DEV / SQRT(N);
ZUPPER = MEAN + PROBIT(1 - &ALPHA / 2) * STD_DEV / SQRT(N);
ZTS = (MEAN - &MUZERO) / (STD_DEV / SQRT(N));
IF &SIDE = 'L' THEN ZPVALUE = PROBNORM(ZTS);
IF &SIDE = 'U' THEN ZPVALUE = 1 - PROBNORM(ZTS);
IF &SIDE = 'B' THEN ZPVALUE = 2 * MIN(1 - PROBNORM(ZTS), PROBNORM(ZTS));
RUN;
PROC PRINT DATA=ZTEST NOOBS;
VAR N MEAN STD_DEV ZLOWER ZUPPER ZPVALUE;
TITLE 'Z-INTERVALS';
RUN;Step 3
DATA TIME;
INPUT TIME @@;
DATALINES;
43 90 84 87 116 95 86 99 93 92
121 71 66 98 79 102 60 112 105 98
;
RUN;
%MACRO ZTEST_SELF(D, V, ALPHA, MUZERO, SIDE);
PROC MEANS DATA=&D;
VAR &V;
OUTPUT OUT=ZDATA MEAN=MEAN N=N STDDEV=STD_DEV;
RUN;
DATA ZTEST;
SET ZDATA;
CL = (1 - &ALPHA) * 100;
ZLOWER = MEAN - PROBIT(1 - &ALPHA / 2) * STD_DEV / SQRT(N);
ZUPPER = MEAN + PROBIT(1 - &ALPHA / 2) * STD_DEV / SQRT(N);
ZTS = (MEAN - &MUZERO) / (STD_DEV / SQRT(N));
IF &SIDE = 'L' THEN ZPVALUE = PROBNORM(ZTS);
IF &SIDE = 'U' THEN ZPVALUE = 1 - PROBNORM(ZTS);
IF &SIDE = 'B' THEN ZPVALUE = 2 * MIN(1 - PROBNORM(ZTS), PROBNORM(ZTS));
RUN;
PROC PRINT DATA=ZTEST NOOBS;
VAR N MEAN STD_DEV CL ZLOWER ZUPPER ZPVALUE;
TITLE 'Z-INTERVALS & TESTS';
RUN;
%MEND ZTEST_SELF;
%ZTEST_SELF(TIME, TIME, 0.05, 80, 'B');Example Macro Call
%ZTEST_SELF(TIME, TIME, 0.05, 80, 'B');- TIME (first): input dataset
- TIME (second): variable of interest
- 0.05: significance level α
- 80: hypothesized mean μ₀
- ‘B’: two-sided Z-test
- ‘L’ = left-tailed
- ‘U’ = right-tailed
- ‘B’ = two-tailed
Summary
By following the three-step macro workflow, you have:
- Written correct and verifiable SAS code,
- Introduced flexibility using macro parameters,
- Built a reusable SAS macro for Z-test inference.
This same workflow applies directly to:
- t-tests,
- confidence intervals,
- ANOVA summaries,
- regression diagnostics.
Once you master this pattern, writing SAS macros becomes systematic, reusable, and scalable, just like functions in R or Python.