4.5 Processing Data Iteratively with DO Loops
Key Takeaways
- DO loops exist to eliminate redundant code and perform repetitive calculations: twelve near-identical assignment statements collapse into one indexed loop that scales when columns are added.
- An iterative DO loop executes a block of SAS statements repeatedly as an index variable increments from a specified start value to a stop value by a defined increment.
- Upon exiting an iterative DO loop, the index variable retains a value equal to the stop value PLUS the increment value (e.g., DO i=1 TO 10; leaves i=11 after loop exit).
- A DO WHILE (condition) loop evaluates its expression BEFORE executing the loop body (pre-test); if the condition evaluates to false on entry, the loop body executes zero times.
- A DO UNTIL (condition) loop evaluates its expression AFTER executing the loop body (post-test); the loop body ALWAYS executes at least once regardless of initial condition state.
4.5 Processing Data Iteratively with DO Loops
Quick Answer:
DOloops allow SAS to execute a block of code repetitively. An iterativeDOloop uses an index variable that increments from a start value to a stop value. ADO WHILEloop repeats while a condition remains true (evaluating before each execution). ADO UNTILloop repeats until a condition becomes true (evaluating after each execution, guaranteeing at least one execution).
Iterative DO Loops Mechanics
An iterative DO loop executes statements a specified number of times using an index variable.
Syntax
DO index-variable = start TO stop <BY increment>;
/* SAS statements to execute repeatedly */
END;
index-variable: A numeric variable created in the PDV that tracks iteration count.start: Initial value of the index variable.stop: Target boundary value.BY increment: Optional increment step size (default is1).
data work.table_of_squares;
do i = 1 to 5;
Square = i ** 2;
end;
run;
CRITICAL EXAM CONCEPT: Index Variable Value Upon Exit
One of the most frequently tested questions on the SAS Base Specialist exam asks for the value of the index variable after an iterative DO loop finishes.
Let me trace do i = 1 to 5; step-by-step:
- Iteration 1:
iis set to1.1 <= 5is true.Square = 1.ENDincrementsito2. - Iteration 2:
iis2.2 <= 5is true.Square = 4.ENDincrementsito3. - Iteration 3:
iis3.3 <= 5is true.Square = 9.ENDincrementsito4. - Iteration 4:
iis4.4 <= 5is true.Square = 16.ENDincrementsito5. - Iteration 5:
iis5.5 <= 5is true.Square = 25.ENDincrementsito6. - Loop Evaluation: SAS checks
i <= 5(6 <= 5). Condition is FALSE. Loop terminates!
Golden Rule: When an iterative
DOloop terminates normally, the index variable's stored value in the PDV is equal tostop + increment(e.g.,5 + 1 = 6).
Eliminating Redundant Code and Repetitive Calculations
The content guide states the practical purpose of iterative loops explicitly: use DO loops to eliminate redundant code and to perform repetitive calculations. Any time the same statement is written more than twice with only an index changing, a DO loop is the intended replacement.
/* REDUNDANT: twelve near-identical statements, twelve chances to typo */
data work.monthly_adjusted;
set work.monthly_raw;
Mon1 = Mon1 * 1.03;
Mon2 = Mon2 * 1.03;
Mon3 = Mon3 * 1.03;
/* ... nine more lines ... */
Mon12 = Mon12 * 1.03;
run;
/* CONCISE: one loop, one rate to change, no chance of a skipped month */
data work.monthly_adjusted;
set work.monthly_raw;
array mon{12} Mon1-Mon12;
do i = 1 to 12;
mon{i} = mon{i} * 1.03;
end;
drop i;
run;
The second use is the repetitive calculation: generating a series of values that do not exist in the input at all.
/* Ten-year projection generated from a single input row */
data work.amortisation;
Principal = 250000;
Rate = 0.055;
do Year = 1 to 10;
Interest = Principal * Rate;
Principal = Principal - 12000 + Interest;
output; /* one row per projected year */
end;
format Principal Interest dollar12.2;
run;
| Benefit | Why it matters on the exam |
|---|---|
| Fewer lines to mistype | Syntax errors on a performance-based exam cost time you cannot recover |
| One place to change a constant | A rate written twelve times will eventually disagree with itself |
| Scales with the data | do i = 1 to dim(mon); adapts automatically when a column is added |
| Generates rows absent from the input | The only way to build projections, schedules, and simulations in a DATA step |
Explicit OUTPUT Statements Inside DO Loops
By default, a DATA step writes an observation to the output dataset only once per DATA step iteration (when reaching the RUN; statement). Placing an explicit OUTPUT; statement inside a DO loop alters this behavior completely.
Generating Multiple Observations per Data Step Iteration
data work.investment_growth;
Capital = 10000;
Rate = 0.05;
do Year = 1 to 10;
Capital = Capital * (1 + Rate);
output; /* Writes a row FOR EVERY YEAR iteration of the loop! */
end;
run;
In work.investment_growth, the explicit OUTPUT; statement inside the loop outputs 10 observations (one for each year). Furthermore, placing an explicit OUTPUT statement anywhere in a DATA step suppresses the automatic output at the bottom of the DATA step.
Conditional Loops: DO WHILE vs. DO UNTIL
When the number of required iterations is unknown in advance (e.g., calculating how many years until an investment doubles), use conditional DO loops.
DO WHILE Loop (Pre-Test Loop)
Evaluates the condition BEFORE executing the loop body.
DO WHILE (expression);
/* statements */
END;
- Executes as long as
expressionis TRUE. - If
expressionis FALSE on entry, the loop body executes 0 times.
data work.while_example;
Capital = 100000; /* Capital is ALREADY >= 50000 */
Years = 0;
/* Evaluates (Capital < 50000) BEFORE entry -> FALSE! */
do while (Capital < 50000);
Years + 1;
Capital = Capital * 1.05;
end;
run;
/* Result: Years = 0, Capital = 100000 (Loop executed 0 times) */
DO UNTIL Loop (Post-Test Loop)
Evaluates the condition AFTER executing the loop body.
DO UNTIL (expression);
/* statements */
END;
- Executes as long as
expressionis FALSE, terminating when it becomes TRUE. - Because the test occurs at
END, the loop body ALWAYS executes at least ONCE.
data work.until_example;
Capital = 100000; /* Capital is ALREADY >= 50000 */
Years = 0;
/* Executes body FIRST, then evaluates (Capital >= 50000) -> TRUE! Terminates loop. */
do until (Capital >= 50000);
Years + 1;
Capital = Capital * 1.05;
end;
run;
/* Result: Years = 1, Capital = 105000 (Loop executed 1 time!) */
Nested DO Loops
A DO loop may contain another DO loop. Each nested loop needs its own index variable and its own END; statement, and the inner loop runs to completion on every single iteration of the outer loop. Total iterations equal the product of the two ranges.
/* Build a 3-region x 4-quarter forecasting grid = 12 observations */
data work.forecast_grid;
set work.base_rates; /* one input row: BaseAmount */
do RegionNum = 1 to 3; /* OUTER loop: 3 iterations */
do Quarter = 1 to 4; /* INNER loop: 4 per region */
Projected = BaseAmount * (1 + 0.02 * Quarter) * RegionNum;
output; /* 3 x 4 = 12 rows written */
end; /* closes the INNER loop */
end; /* closes the OUTER loop */
run;
Rules and Traps for Nested Loops
- One
END;perDO— an unmatchedENDmakes SAS swallow the rest of the DATA step and usually produces a confusingERROR: There is an extra END statementor an infinite construct. - Distinct index variables — reusing
ifor both loops causes the inner loop to overwrite the outer counter, so the outer loop never advances correctly. - Where
OUTPUT;sits determines the row count — inside the inner loop you get 12 rows; betweenend;andend;you get 3 rows; after bothend;statements you get 1 row. - Index values after exit — each index still ends at stop + increment (
RegionNum = 4,Quarter = 5) unless you drop them.
Comparison: DO Loop Variants
| Feature | Iterative DO | DO WHILE | DO UNTIL |
|---|---|---|---|
| Test Timing | Before each iteration | Pre-test (Before loop body) | Post-test (After loop body) |
| Termination Condition | Index exceeds stop value | Expression becomes FALSE | Expression becomes TRUE |
| Minimum Iterations | 0 (if start > stop) | 0 iterations | 1 iteration (Guaranteed) |
| Index Variable | Managed automatically | Managed manually inside loop | Managed manually inside loop |
| Common Use Case | Fixed counts, arrays | Condition monitoring (0+ runs) | Condition monitoring (1+ runs) |
Combining Iterative and Conditional Clauses
You can combine iterative index processing with WHILE or UNTIL clauses to set safety limits and prevent infinite loops:
data work.safe_projection;
Capital = 10000;
/* Runs for a maximum of 30 years OR UNTIL Capital reaches 50,000 */
do Year = 1 to 30 until (Capital >= 50000);
Capital = Capital * 1.07;
end;
run;
Consider the following SAS DATA step:
How many observations are written to data work.test;
do i = 1 to 5;
Square = i * i;
end;
run;
work.test, and what is the value of i stored in the output dataset?
A financial analyst wants to calculate compound interest until capital reaches $100,000, ensuring that the calculation loop executes AT LEAST ONCE even if initial capital is already $100,000 or more. Which DO loop structure guarantees at least one execution?
What is the primary effect of placing an explicit OUTPUT; statement inside an iterative DO loop in a SAS DATA step?
Consider the following DO WHILE loop code segment:
What are the final values of data work.growth;
Invest = 1000;
Year = 0;
do while (Invest < 1000);
Year + 1;
Invest = Invest * 1.05;
end;
run;
Year and Invest stored in work.growth?