Pyrite | Level 9

## Iterative Do Loops - Summining

Lets say I have a table called: pg2. savings looking like this:

 Obs Name Amount 1 James 250
 2 Linda 300
 3 Mary 275
 4 Robert 350

If I now run the code:

```data YearSavings;
set pg2.savings;
*savings = 0; *This will solve the problem of question 1 below.;
do Month=1 to 12;
Savings+Amount;
Savings+(Savings*0.02/12);
end;
format Savings comma12.2;
*Month = Month-1; *Related to question 2;
run;```
I get the following output:

 Obs Name Amount Month Savings 1 James 250 13 3,032.70
 2 Linda 300 13 6,733.15
 3 Mary 275 13 10,205.03
 4 Robert 350 13 14,656.79

Notice how the Savings column clearly is wrong (for example, Mary saves less than Linda but still ends up with more savings).

The explanation for this is:

"Notice that the Savings value keeps increasing for each row. This is because the value of Savings is automatically retained because it's part of a sum statement. So each time we start a new iteration of the data step, reading a new person, the value of Savings is retained, but it represents the savings from the previous person."

I have though about it but do not understand it.

The solution is to un-comment the row:
`*savings = 0; *This will solve the problem of question 1 below.;`

So my questions is:

1. Any explanations of why the problem occurs? I do not understand this nor the explanation given above.
2. Despite running 12 months in the iterations, it shows up as 13 which is pretty uggly. One solution would be to un-comment  the row:
`*Month = Month-1;`
Is there any best practices for this?

Thanks.

1 ACCEPTED SOLUTION

Accepted Solutions

## Re: Iterative Do Loops - Summining

Hello @SasStatistics,

@SasStatistics wrote:
2. Despite running 12 months in the iterations, it shows up as 13 which is pretty uggly. One solution would be to un-comment  the row:
`*Month = Month-1;`
Is there any best practices for this?

It is this additional incrementation of the index variable beyond the stop value (here: 12) which causes the loop to stop iterating. Sometimes this can be useful, e.g., because the final value of the index variable shows that the loop did not stop during the last iteration or earlier (example).

There are several reasons why in practice those "ugly" values are rarely an issue (that requires a statement like Month = Month-1):

1. The index variables of many if not most iterative DO loops (of the form DO variable=start TO stop) are only used within the loop and not written to any output dataset. In these cases their names (typically i, j, etc.) often occur in DROP statements or DROP= dataset options:
```data ...(drop=i);
...
do i=1 to ...;
...
end;
...
run;```
2. In many cases where the value of the index variable is written to the output dataset, this is done by an OUTPUT statement within the loop. This, in turn, overrides the implicit OUTPUT at the end of the DATA step iteration, so that the "ugly" last value is automatically left out. Insert an OUTPUT statement into your DO loop and you'll get 4*12=48 observations from your sample data and the assignment statement for Month can be deleted.
3. Other common types of DO loops use UNTIL or WHILE conditions (or a LEAVE statement) preventing the index variable (if any) from being incremented beyond the stop value. In your example you could change the DO statement to either of the following forms and again omit the final assignment statement for Month.
`do Month=1 by 1 until(Month=12);`
`do Month=1 by 1 while(Month<=12);`
3 REPLIES 3
Pyrite | Level 9

## Re: Iterative Do Loops - Summining

Regarding question 1 I now understand it...

From the line "set pg2.savings;" a new row is read after each loop and after that we need to put savings = 0; .

So question 1 is Done!

## Re: Iterative Do Loops - Summining

Hello @SasStatistics,

@SasStatistics wrote:
2. Despite running 12 months in the iterations, it shows up as 13 which is pretty uggly. One solution would be to un-comment  the row:
`*Month = Month-1;`
Is there any best practices for this?

It is this additional incrementation of the index variable beyond the stop value (here: 12) which causes the loop to stop iterating. Sometimes this can be useful, e.g., because the final value of the index variable shows that the loop did not stop during the last iteration or earlier (example).

There are several reasons why in practice those "ugly" values are rarely an issue (that requires a statement like Month = Month-1):

1. The index variables of many if not most iterative DO loops (of the form DO variable=start TO stop) are only used within the loop and not written to any output dataset. In these cases their names (typically i, j, etc.) often occur in DROP statements or DROP= dataset options:
```data ...(drop=i);
...
do i=1 to ...;
...
end;
...
run;```
2. In many cases where the value of the index variable is written to the output dataset, this is done by an OUTPUT statement within the loop. This, in turn, overrides the implicit OUTPUT at the end of the DATA step iteration, so that the "ugly" last value is automatically left out. Insert an OUTPUT statement into your DO loop and you'll get 4*12=48 observations from your sample data and the assignment statement for Month can be deleted.
3. Other common types of DO loops use UNTIL or WHILE conditions (or a LEAVE statement) preventing the index variable (if any) from being incremented beyond the stop value. In your example you could change the DO statement to either of the following forms and again omit the final assignment statement for Month.
`do Month=1 by 1 until(Month=12);`
`do Month=1 by 1 while(Month<=12);`
Super User

## Re: Iterative Do Loops - Summining

If you really want to keep MONTH to indicate how many months ahead the calculated amount represents then use a different variable for the loop counter.

``````months=12 ;
do i=1 to months ;
....
end;
drop i;``````

Discussion stats
• 3 replies
• 816 views
• 2 likes
• 3 in conversation