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:

  1. Understand the logic behind SAS macro programming.
  2. Identify macro inputs and outputs.
  3. Write simple SAS macros for statistical analysis.
  4. 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 %LET variables into macro arguments.
  • Place the code inside %MACRO ... %MEND.
  • Remove the %LET statements.
  • 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:

  1. Write the first macro to compute the sample mean, standard deviation as well as the \(p\)-value
  2. 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:

  • &alpha is the significance level
  • &muzero is 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:

  1. Written correct and verifiable SAS code,
  2. Introduced flexibility using macro parameters,
  3. 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.