DATA Step, Macro, Functions and more

Accessing variable condition on macro variable value in loop

Reply
Occasional Contributor
Posts: 10

Accessing variable condition on macro variable value in loop

Hello,

I'm having trouble with the last several lines of the macro I'm attempting to build. For each observation in my data set, the macro chooses a category "n" (0 through 6) based on the outcome of a random number draw. The last several lines access variables whose suffix depends on that category (e.g., "load_factor_2" or "seats_per_plane_2", etc.).

Before doing the intended calculations using these variables, I've temporarily inserted two lines to find out whether the correct values are being accessed using the suffix "n". What actually seems to be happening is that for every observation, "&n" as a suffix resolves to 0, and the value of "n" itself is added to that. So in a case where n = 2, load_factor_test calculates load_factor_0 + 2. The value of load_factor_0 is correctly accessed for that observation.....but I don't understand what's prompting the addition. And I'm at a loss for how to get access to the value of load_factor_2. (I've also tried inserting the value into a macro variable, and tried _&&n as a suffix, in case this is necessary. Both produce the same result.)

I'm sure there are much more expeditious ways to design this entire piece of code, but this is what I've come up with. Eventually, I'd like to translate the "do" loop dependent on the value of "condition" into a "do while" loop (so that "condition" is recalculated and the loop is run until it reaches 0), but I'm having trouble with the simpler step first.

Any help would be greatly appreciated.

%macro test;
Data FOS.ACChoiceModelTest; set out_scores;
%let cum_p = 0;
%do i = &min %to &max;
cum_p_&i = &cum_p + P_&i; /* Calculate cumulative probabilities */
%let cum_p = &cum_p + P_&i;
%end;

%do i = 1 %to 10;
ac_cat_&i = .; /* Create empty variables to hold new aircraft selections */
%end;

%let flight_counter = 0; /* Initialize counter: the number of added flights */
empty_seat_counter = sgmt_empty_seats; /* Initialize counter: the number of empty (available) seats */
remaining_pass_counter = add_passenger_demand; /* Initialize counter: the number of passengers not yet emplaned */
condition = remaining_pass_counter >= .25*empty_seat_counter; /* Starting condition for adding flight */

if condition = 1 then do;
%let flight_counter = %eval(&flight_counter + 1); /* Add a flight */
%let rand = uniform(-1); /* Random number draw */
rand = &rand;
%let n = 0;
%do i = 0 %to 6;
%let n = &n + (rand >= cum_p_&i); /* Use the random number draw to find the chosen SC category */
%end;
ac_cat_&flight_counter = &n; /* Record the AC choice in a permanent variable */

load_factor_test = load_factor_&n;
seats_per_plane_test = seats_per_plane_&n;

remaining_pass_counter = remaining_pass_counter - (load_factor_&n * seats_per_plane_&n); /* Recalculate remaining passengers */
empty_seat_counter = sgmt_empty_seats + (1 - (load_factor_&n * seats_per_plane_&n)); /* Recalculate empty seats */
condition = remaining_pass_counter >= .25*empty_seat_counter; /* Recalculate condition for loop */
end;

run;
%mend test;
%test
SAS Super FREQ
Posts: 8,744

Re: Accessing variable condition on macro variable value in loop

Hi:
Do you have a working, non-macroized SAS program that produces what you want?? I mean a program without ANY macro %DO loops or &macvar references???

Here's the thing. Remember a few posts back
http://support.sas.com/forums/message.jspa?messageID=48254#48254

When I posted some useful links about macro processing:
http://www2.sas.com/proceedings/sugi28/056-28.pdf
http://www2.sas.com/proceedings/sugi29/128-29.pdf (this paper has a good discussion/examples on IF versus %IF)
http://www2.sas.com/proceedings/sugi27/p067-27.pdf (the last sentence in this paper is: "Often in DATA step programming, arrays provide a better answer than macro.")
http://analytics.ncsu.edu/sesug/2006/HW02_06.PDF

and I ended the post by saying:
"You also need to REALLY understand the difference between macro program compile, macro program execution phases and do NOT confuse them with DATA step compile and DATA step execution phases."


The macro facility is going to ONLY, ONLY, ONLY generate code that will be sent forward to the compiler. So, can you tell me what DATA step code you envision being sent to the compiler as a result of this %DO loop and assignment statement from your program??
[pre]
%let n = 0;
%do i = 0 %to 6;
%let n = &n + (rand >= cum_p_&i); /* Use the random number draw to find the chosen SC category */
%end;
ac_cat_&flight_counter = &n; /* Record the AC choice in a permanent variable */
[/pre]

By the time this program gets sent to the compiler ALL the macro variable references and %DO loop and %LET will be gone, resolved, or otherwise, not in the code that the compiler gets.

Have you used SYMBOLGEN, MPRINT, MLOGIC options to review the compiled code that has been generated from your macro program??? Is the generated code the same as the working SAS code that you started with (or should have started with)????

cynthia
Occasional Contributor
Posts: 10

Re: Accessing variable condition on macro variable value in loop

Whew - okay, point taken. I apologize; I hadn't seen your previous post. I've been looking over the documentation you refer to, and I'm working on understanding distinction between how and when macros are processed (versus normal SAS code), and what that means for the process I'm trying to implement.....

Backing up and simplifying as you suggest, I separated out the stages, tried to use variables rather than macro variables wherever possible, and omit the loops except where I know they work as intended. (I don't actually know how to avoid using loops in %do / %end form [such as using do / end instead], and these steps function as intended, so I left these in.)

I know the first portion of the code (which just generates empty variables for use later, and calculates cumulative probabilities) works, so I've separated that out:

%macro c;
Data FOS.ACChoiceModelTest; set out_scores;
%let cum_p = 0;
%do i = &min %to &max;
cum_p_&i = &cum_p + P_&i; /* Calculate cumulative probabilities */
%let cum_p = &cum_p + P_&i;
%end;
%do i = 1 %to 10;
ac_cat_&i = .; /* Create empty variables to hold new aircraft selections */
%end;
run;
%mend c;
%c


What I'm stuck on now is how to change the value of a variable, or access its values for calculations, if the name of that variable depends upon the value of a macro variable. So in the following code, everything works as intended, except the last two lines. I know that the macro variables "f" and "n" have been successfully initialized, since the variable "test_f" and "test_n" take on the correct values, but I can see from the log that "ac_cat_&f" is read as "ac_cat_", and "load_factor_&n" is read as "load_factor_". Why is it not permitted to use macro variables when referring to an existing variable? I don't understand what I'm doing wrong here.....



Data FOS.ACChoiceModelTest2; set FOS.ACChoiceModelTest;
flight_counter = 0; /* Initialize counter: the number of added flights */


/* do while(condition = 1) ; */
if condition = 1 then do ;

flight_counter = flight_counter + 1; /* Add a flight */
n = 0; /* Variable to store the chosen AC category */

%let rand = uniform(-1); /* Random number draw */

%macro d;
%do i = 0 %to 6;
n = n + (&rand >= cum_p_&i); /* Use the random number draw to find the chosen SC category */
%end;
%mend d;
%d

call symput('f', flight_counter);
call symput('n', n);
test_f = &f;
test_n = &n;

ac_cat_&f = n;
load_factor_test = load_factor_&n;
end;
run;



Thank you very much for your help, Cynthia.
SAS Super FREQ
Posts: 8,744

Re: Accessing variable condition on macro variable value in loop

Hi:
You still have not simplified your code to the point where no %DO loops (zip, nada, none, nary a one) are included. If you have to type 6 or 7 assignment statements to understand what your program needs to look like, then type them. You really, really need to understand the code that needs to be generated. I am still not convinced that you need a Macro program. I think you may be confusing iterative ARRAYS and numbered variables with MACRO %DO loops.

Also, do you understand the difference between DATA step compile time and DATA step execution time??? And what happens in each of these phases of a simple program??? Do you understand the difference between an &macvar reference, a call symput as far as the creation of macro variables??? Do you understand what a macro program %macpgm really is and how it ONLY generates code that will get sent forward to the compiler????

OK, then, once you understand that, you have to understand that the SAS Macro facility has a word scanning and macro trigger resolution phase that happens before ANY code is compiled by the "regular" compiler. By the time the word scanning and macro trigger resolution phase is over there are NO &macvar or %macpgm references in your program. So for just one example,...a %LET out in open code will get executed one time, and only one time -- when code is compiled by the "regular" compiler. A %LET statement inside a macro program will work a bit differently, but essentially, a %LET statement does not "communicate" with an executing DATA step program.

Do NOT try to make a shortcut with macros until you understand, really understand, what your working, non-macroized program, looks like. You can also make sure that your program is error free before you go forward with a macro approach. (Because it is very hard to separate out whether your error messages come from your original program or your generated code.) I think that you really need to take a step back and stop thinking about Macro %DO loops and look at ARRAY processing.

For example, the 2 programs below show the difference with a DATA step DO loop WITHOUT an ARRAY and WITH an ARRAY. No macros involved. Which DATA step program -- just for the calculation of "NEWN" variables-- is closer to what you want/need to do?? (LOG output is shown with the result of the PUTLOG statements.)

This is a good introduction to ARRAY processing.
http://support.sas.com/rnd/papers/sgf07/arrays1780.pdf

cynthia
[pre]
NO ARRAYS/NO MACRO
5253 data testit;
5254 ** make the VARN and NEWN variables without any macros;
5255 do i = 0 to 6;
5256 putlog '----- INSIDE TOP of LOOP ' _n_= '|' i= '|' ;
5257 x=uniform(-1);
5258 varn=i + 10;
5259 newn = varn + x;
5260 putlog '----- INSIDE BOTTOM of LOOP ' _n_= '|' i= '|' varn= '|' x= '|' newn= '|';
5261 putlog ' ';
5262 end;
5263
5264 putlog '-----what is in TESTIT -- ONLY 1 OBS';
5265 putlog '-----OUTSIDE of LOOP ';
5266 putlog _all_;
5267 putlog '*************************************************';
5268 run;

----- INSIDE TOP of LOOP _N_=1 |i=0 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=0 |varn=10 |x=0.0328104845 |newn=10.032810485 |

----- INSIDE TOP of LOOP _N_=1 |i=1 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=1 |varn=11 |x=0.7854493804 |newn=11.78544938 |

----- INSIDE TOP of LOOP _N_=1 |i=2 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=2 |varn=12 |x=0.5329144548 |newn=12.532914455 |

----- INSIDE TOP of LOOP _N_=1 |i=3 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=3 |varn=13 |x=0.2107451191 |newn=13.210745119 |

----- INSIDE TOP of LOOP _N_=1 |i=4 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=4 |varn=14 |x=0.1058570995 |newn=14.105857099 |

----- INSIDE TOP of LOOP _N_=1 |i=5 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=5 |varn=15 |x=0.2827750655 |newn=15.282775066 |

----- INSIDE TOP of LOOP _N_=1 |i=6 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=6 |varn=16 |x=0.7087042163 |newn=16.708704216 |

-----what is in TESTIT -- ONLY 1 OBS
-----OUTSIDE of LOOP
i=7 x=0.7087042163 varn=16 newn=16.708704216 _ERROR_=0 _N_=1
*************************************************
NOTE: The data set WORK.TESTIT has 1 observations and 4 variables.
NOTE: DATA statement used (Total process time):
real time 0.04 seconds
cpu time 0.01 seconds
[/pre]

versus

[pre]
WITH ARRAY/NO MACRO
5270 data testit2;
5271 array new{0:6} newn0-newn6;
5272 ** use an array;
5273 do i = 0 to 6;
5274 putlog '----- INSIDE TOP of LOOP ' _n_= '|' i= '|' ;
5275 x=uniform(-1);
5276 varn=i + 10;
5277 new(i) = varn + x;
5278 putlog '----- INSIDE BOTTOM of LOOP ' _n_= '|' i= '|' varn= '|' x= '|' new(i)= '|';
5279 putlog ' ';
5280 end;
5281
5282 putlog '-----what is in TESTIT2 -- ONLY 1 OBS, but now have variables NEWN0-NEWN6';
5283 putlog '-----OUTSIDE of LOOP ' ;
5284 putlog _all_;
5285 putlog '*************************************************';
5286 run;

----- INSIDE TOP of LOOP _N_=1 |i=0 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=0 |varn=10 |x=0.1132566063 |newn0=10.113256606 |

----- INSIDE TOP of LOOP _N_=1 |i=1 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=1 |varn=11 |x=0.7052701673 |newn1=11.705270167 |

----- INSIDE TOP of LOOP _N_=1 |i=2 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=2 |varn=12 |x=0.8287894241 |newn2=12.828789424 |

----- INSIDE TOP of LOOP _N_=1 |i=3 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=3 |varn=13 |x=0.3050844866 |newn3=13.305084487 |

----- INSIDE TOP of LOOP _N_=1 |i=4 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=4 |varn=14 |x=0.1069306373 |newn4=14.106930637 |

----- INSIDE TOP of LOOP _N_=1 |i=5 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=5 |varn=15 |x=0.9160557245 |newn5=15.916055724 |

----- INSIDE TOP of LOOP _N_=1 |i=6 |
----- INSIDE BOTTOM of LOOP _N_=1 |i=6 |varn=16 |x=0.0842098724 |newn6=16.084209872 |

-----what is in TESTIT2 -- ONLY 1 OBS, but now have variables NEWN0-NEWN6
-----OUTSIDE of LOOP
newn0=10.113256606 newn1=11.705270167 newn2=12.828789424 newn3=13.305084487 newn4=14.106930637
newn5=15.916055724 newn6=16.084209872 i=7 x=0.0842098724 varn=16 _ERROR_=0 _N_=1
*************************************************
NOTE: The data set WORK.TESTIT2 has 1 observations and 10 variables.
NOTE: DATA statement used (Total process time):
real time 0.09 seconds
cpu time 0.03 seconds
[/pre]
Occasional Contributor
Posts: 10

Re: Accessing variable condition on macro variable value in loop

Hi,

Thanks for your reply. The programs you display are somewhat similar to what I need to achieve; I think the one below does the actual calculation I want in the array format that you recommend.

Nonetheless, the first issue I really need to resolve before I can continue is how to use the value of a variable (in my case, the value of the variable "flight_counter") to call up another variable to be changed (with the prefix ac_cat_, and a suffix dependent on "flight_counter". So, for example, when flight_counter = 1, I need to insert the value of "n" into the variable "ac_cat_1" ; when flight_counter = 2, insert the value of "n" into "ac_cat_2" ; and so on. The set of variables prefixed "ac_cat_" is created, and filled with ".". I need a statement before the end of the "if" statement to populate the appropriate "ac_cat_" variable with the value of "n". For now, I have inserted the line "ac_cat_1 = n;", but the "1" needs to be changed to a reference to the value of "flight_counter". This is the information that I absolutely need to store from the output of the process below. As I mentioned in an earlier post, I would eventually like to use a "do while" loop, changing "condition" at the end of each turn of the loop, such that the process is repeated until "condition" reaches 0. This will require "flight_counter" to increase an unknown number of times.




data mydata_out; set mydata ;
flight_counter = 0;
empty_seat_counter = sgmt_empty_seats;
remaining_pass_counter = add_passenger_demand;
condition = remaining_pass_counter >= .25*empty_seat_counter;

if condition = 1 then do;
drop i ;
flight_counter = flight_counter + 1;
array cum_p {0:6} cum_p_0 - cum_p_6;
array lf {0:6} load_factor_0 - load_factor_6;
array choose{0:6};

do i = 0 to 6 ;
x = uniform(-1) ;
choose{i} = x >= cum_p{i} ;
end;
n = n = sum(of choose{*});

load_factor_chosen = lf{n} ;
ac_cat_1 = n;

end;

run;



I realize that I have quite a bit to learn about macros the details of SAS processing, and that seems to have ignited a bit of indignation.....However, I would really appreciate if someone could provide insight into this particular issue (which is probably quite simple), since figuring that out is crucial for me.

Thanks very much,
R.

I am going to repost this message under a new thread, since my current question has little to do with the initial issue.

Message was edited by: rdwest
SAS Super FREQ
Posts: 8,744

Re: Accessing variable condition on macro variable value in loop

Hi:
Sorry -- previous posts were not signs of indignation -- I tried to be emphatic enough to prompt you do some reading and understanding before you went down a road that might, in the end, prove to be fruitless.

The index for an ARRAY reference does NOT need to be I or the variable used to iterate a DO loop. For example, a very handy method for referencing an ARRAY member is to use another variable. For example:
[pre]
array acc AC_CAT_1-AC_CAT_200;
n = some calc;
flight_number=1;
acc(flight_number) = n; /* however you calculate N */
[/pre]

In this instance, when the value of FLIGHT_NUMBER is 1, then AC_CAT_1 would get assigned the current value of N, however you calculated it.

On pages 7 and 8 of the Array paper I referenced above, is an different example, where the data has a variable called EXPENSE, which is a code that represents an expense type from 1 to 6. And, then in the program, the value of the TYPEOFEXPENSE variable is set using the value of the EXPENSE variable as the index for the NAME array.

The fact is that your FLIGHT_NUMBER could be calculated from some computation in your program or from some variable in your dataset, and as long as the generated value was an integer number, the variable value CAN be used as an array index. That would mean that your numbered AC_CAT_x variables would also have to be declared as an array. It doesn't matter whether you know the number of array elements that you will have in the beginning -- the easiest thing to do is to just make a really big array that will hold all the possible AC_CAT_x variables that your program could create.

Consider the program below. The calculation for FLIGHT_NUMBER is inside a DO LOOP, but the value of FLIGHT_NUMBER is set based on other variables. The AGE and HEIGHT variables are used (in a rather silly way) to create the FLIGHT_NUMBER variable. I just used calculations that would result in a number between 1 and 10. Then, the value for my AC_CAT_x variable is being set, on each iteration through the loop, to some RANUNI value -- but as you can see, for Alfred's observation, the generated FLIGHT_NUMBER values are 1, 4 and 6, while for Barbara's observation (obs #3), the generated FLIGHT_NUMBER values are 1, 3 and 6. For each observation, different AC_CAT_x variables will be "filled" with a random number based on the expression used to calculate FLIGHT_NUMBER.

In addition, the program shows how to sum up numbered variables used in an ARRAY -- by using the SUM function with the OF and a variable list. For my ARRAY, there are some array members that have missing values and that's OK because the SUM function ignores missing values, so my TOTACC variable is the sum of only the array members that have values. The TOTACC2 variable shows an alternate way to sum up the members of an ARRAY.

Useful functions that were designed for ARRAYS are the DIM, HBOUND and LBOUND functions. I haven't used them in my program, but they are mentioned in the doc and in the papers below.

Some other good papers with ARRAY examples:
http://support.sas.com/resources/papers/proceedings09/032-2009.pdf (more examples of using other variables as an array index)
http://support.sas.com/resources/papers/proceedings09/155-2009.pdf (how to use arrays in DO loops)
http://www2.sas.com/proceedings/sugi29/158-29.pdf (some examples of multi-dimensional arrays and coding techniques)

cynthia
[pre]
578 data makedata;
579 set sashelp.class(obs=3);
580 array acc ac_cat_1-ac_cat_10;
581 putlog '**** start DO loop';
582 putlog _n_= name= age= height=;
583 ** Even though i goes from 1 to 3, FLIGHT_NUMBER is calculated by performing;
584 ** some calculation using AGE and HEIGHT -- for the first 3 obs, the numbers generated;
585 ** for FLIGHT_NUMBER will fall between 1 and 7 --;
586 ** so I made my ARRAY bigger than 7.;
587 do i = 1 to 3 by 1;
588 if i = 1 then flight_number = round((age/10),1);
589 else if i = 2 then flight_number = floor(((age/10)*3));
590 else if i = 3 then flight_number = floor((height/10));
591
592 acc(flight_number) = ranuni(age + i);
593 putlog ' ' i= flight_number= acc(flight_number)=;
594 end;
595 ** Now total up all array members and with SUM function, missing values will be ignored;
596 totacc = sum(of ac_cat_1-ac_cat_10);
597 totacc2 = sum(of acc(*));
598 putlog 'Outside Loop, TOTAL of all array members is): ' totacc=;
599 putlog 'Alternate Method of summing array members: ' totacc2;
600 putlog '**** output observation';
601 putlog ' ';
602 run;

**** start DO loop
_N_=1 Name=Alfred Age=14 Height=69
i=1 flight_number=1 ac_cat_1=0.7744385473
i=2 flight_number=4 ac_cat_4=0.5513307348
i=3 flight_number=6 ac_cat_6=0.9973645913
Outside Loop, TOTAL of all array members is): totacc=2.3231338734
Alternate Method of summing array members: 2.3231338734
**** output observation

**** start DO loop
_N_=2 Name=Alice Age=13 Height=56.5
i=1 flight_number=1 ac_cat_1=0.8909796806
i=2 flight_number=3 ac_cat_3=0.824038668
i=3 flight_number=5 ac_cat_5=0.5391602463
Outside Loop, TOTAL of all array members is): totacc=2.2541785949
Alternate Method of summing array members: 2.2541785949
**** output observation

**** start DO loop
_N_=3 Name=Barbara Age=13 Height=65.3
i=1 flight_number=1 ac_cat_1=0.1446875972
i=2 flight_number=3 ac_cat_3=0.9753758423
i=3 flight_number=6 ac_cat_6=0.7469103931
Outside Loop, TOTAL of all array members is): totacc=1.8669738327
Alternate Method of summing array members: 1.8669738327
**** output observation
NOTE: There were 3 observations read from the data set SASHELP.CLASS.
NOTE: The data set WORK.MAKEDATA has 3 observations and 19 variables.
NOTE: DATA statement used (Total process time):
real time 0.29 seconds
cpu time 0.03 seconds
[/pre]
Occasional Contributor
Posts: 10

Re: Accessing variable condition on macro variable value in loop

Thank you, Cynthia - that's exactly what I needed! I think I was confused by the fact that the values of macro variables couldn't be used in the calling up the name of a variable, and I overlooked the fact that array values can be. And, furthermore, I don't think I realized that arrays are created directly as variables in the dataset (I thought they were stored for use in the code, rather than generated as variables). Using arrays makes this much more straightforward. I did as you suggested an used the value of flight_counter (as tracked through the loop by "f") to index the variables AC_CAT_f.


Thank you VERY much for all of your advice -- the array documentation is very helpful.

Best,
R.

data mydata_out; set mydata ;
%let y = 5; /* Number of times to repeat loop */
flight_counter = 0; /* Initialize flight counter */

empty_seat_counter = sgmt_empty_seats; /* Initial variable values */
remaining_pass_counter = add_passenger_demand;
condition = remaining_pass_counter >= .25*empty_seat_counter; /* Condition for additional plane selection */

array ac_cat_{&y}; array lf_chosen_{&y}; array spp_chosen_{&y}; /* Define arrays */
array new_dep_perf_{0:6};
drop k; do k = 0 to 6;
new_dep_perf_{k} = 0;
end;

drop f;
do f = 1 to &y;
if condition = 1 then do;

flight_counter = flight_counter + 1;

array cum_p {0:6} cum_p_0 - cum_p_6;
array lf {0:6} load_factor_0 - load_factor_6;
array spp {0:6} seats_per_plane_0 - seats_per_plane_6;

array choose{0:6}; /* Empty array for AC choices */

drop i ;
do i = 0 to 6 ;
x = uniform(-1) ; /* Random number draw */
choose{i} = x >= cum_p{i} ;
end;
n = sum(of choose{*}); /* Calculate chosen AC category */

ac_cat_{f} = n; /* Record chosen AC category */
lf_chosen_{f} = lf{n} ; /* Record corresponding load factor */
spp_chosen_{f} = spp{n}; /* Record corresponding seats per plane */

remaining_pass_counter = remaining_pass_counter - (lf{n} * spp{n}); /* Recalculate remaining passengers */
empty_seat_counter = sgmt_empty_seats + (1 - (lf{n} * spp{n})); /* Recalculate empty seats */
condition = remaining_pass_counter >= .25*empty_seat_counter; /* Recalculate condition for loop */

new_dep_perf_{n} = new_dep_perf_{n} + 1;
end;

end;

run;
SAS Super FREQ
Posts: 8,744

Re: Accessing variable condition on macro variable value in loop

Hi:
Yes, that's almost exactly right. Most folks who have ordered or numbered variables, such as VAR1-VAR10 -- think of them almost intuitively as belonging in an array structure.

For SAS datasets, the numbered variable names are what is permanently stored in the physical data. While a DATA step program is active, however, you can build an ARRAY reference, such as:
[pre]
array myvar var1-var10;
[/pre]

and then you can refer --in the program and ONLY in the program to
[pre]
myvar(1) or myvar(i) or myvar(flight_number)
[/pre]

Once the variables are stored back in the physical storage location again, if you do a PROC CONTENTS, you will see only VAR1-VAR10 -- you will never see a reference to the "myvar" array. Some data base and other file format structures do store the array name physically as a data structure. However, SAS does not.

In fact, in one program, you could have
[pre]
array myvar var1-var10

and in a different program have the reference

array xyz var1-var10;
[/pre]

and, even though "numbered" variables are the most frequent reason for using ARRAYS and DO loops in a DATA step program, you can also declare a group of "unnumbered" variables to be in an ARRAY for purposes of processing. For example:
[pre]
array nvar (*) _numeric_; (all numeric variables in an array)
array cvar (*) _character_; (all character variables in an array)
[/pre]

Note how, in the program below, you do not see any "numbered" variables from the result of the PUTLOG. Even though I referenced cvar(i) and nvar(i) in the DO loop, the log shows you the actual names of the variables, not the "array" reference:
[pre]
650 data tryarr;
651 set sashelp.class(obs=2);
652 array cvar (*) _character_;
653 array nvar (*) _numeric_;
654 putlog 'CVAR Array Ref';
655 do i = 1 to dim(cvar);
656 putlog i= cvar(i)=;
657 end;
658
659 putlog 'NVAR Array Ref';
660 do i = 1 to dim(nvar);
661 putlog i= nvar(i)=;
662 end;
663
664 run;

CVAR Array Ref
i=1 Name=Alfred
i=2 Sex=M
NVAR Array Ref
i=1 Age=14
i=2 Height=69
i=3 Weight=112.5
CVAR Array Ref
i=1 Name=Alice
i=2 Sex=F
NVAR Array Ref
i=1 Age=13
i=2 Height=56.5
i=3 Weight=84
NOTE: There were 2 observations read from the data set SASHELP.CLASS.
NOTE: The data set WORK.TRYARR has 2 observations and 6 variables.
NOTE: DATA statement used (Total process time):
real time 0.03 seconds
cpu time 0.03 seconds
[/pre]

So, there are a lot of neat things that you can do with arrays -- no macros needed. If you do a Google search or a search for user-group and SAS Global Forum papers, you will find a lot more papers that explain the ins and outs of ARRAY processing. There is a place for using macro %DO loops, but it didn't seem that you were at that place.

cynthia
Ask a Question
Discussion stats
  • 7 replies
  • 374 views
  • 0 likes
  • 2 in conversation