USING DATA TO SET MACRO PARAMETERS
UPDATE A PREVIOUS EXAMPLE %macro report(regs=); %let r=1; %let region=%scan(®s,&r); %do %until(®ion eq ); options nodate pageno=1; ods pdf file="®ion..pdf"; Title "Job Cost Reporting for %sysfunc(propcase(®ion)) Region"; proc report data=mysas.projects nowd; where upcase(region) eq "%upcase(®ion)"; column pol_type equipmnt personel jobtotal; define pol_type/group 'Pollutant'; define equipmnt/mean 'Avg. Equipment Cost' format=dollar10.; define personel/mean 'Avg. Personnel Cost' format=dollar10.; define jobtotal/mean 'Avg. Total Cost' format=dollar10.; ods pdf close; %let r=%eval(&r+1); %let region=%scan(®s,&r); %end; %mend;
UPDATE A PREVIOUS EXAMPLE Instead of having the user produce a list of regions for which reports will be generated, we will have our code determine which regions exist and create reports for all of them. Therefore, the information the macro will need to alter through its iterations are contained in the data set many times over, as will usually be the case. Achieving this can be thought of as having the following important characteristics: Need a procedure/method that can produce the set of unique values of the variable. These values may be intermediately stored in a data set for most methods this is required, for some special statements may be helpful. Those values must become values for macro variable(s). A few of our analysis/reporting procedures could be used in this case, none of which is likely easier than PROC FREQ
PULLING A SET OF VALUES FROM A DATA SET This code: proc freq data=mysas.projects; table region; quit; Produces the values we need (plus some we do not): But this is not data what statement can you add to make this a data table?
PULLING A SET OF VALUES FROM A DATA SET Another method you may be less familiar with: proc sort data=mysas.projects out=regions nodupkey; by region;
PULLING A SET OF VALUES FROM A DATA SET Another method you may be less familiar with: proc sort data=mysas.projects out=regions nodupkey; by region; nodupkey removes duplicates of the key value; which is the set of variables you sort by
PULLING A SET OF VALUES FROM A DATA SET Another method you may be less familiar with: proc sort data=mysas.projects out=regions nodupkey; by region; So the sorted data has only 6 observations, corresponding to the 6 different values of region in this case.
PULLING A SET OF VALUES FROM A DATA SET Another method you may be less familiar with: proc sort data=mysas.projects out=regions nodupkey; by region; So the sorted data has only 6 observations, corresponding to the 6 different values of region in this case.
PULLING A SET OF VALUES FROM A DATA SET Another method you may be less familiar with: proc sort data=mysas.projects out=regions nodupkey; by region; For either of these methods, a keep= option on the output data set can be used to limit this to only the values desired.
NOW WHAT? Putting these into a data set should allow for macro variable construction via the call symput routine. We could, using the catx function in the data step, assemble these into a list and then run the remainder of the code exactly as we did previously. But it would be silly to put them together only to immediately separate them again. In this case, we will create a set of macro variables for the set of regions, something like this: data _null_; set regions; call symput(cats('region',put(_n_,1.)),strip(region));
NOW WHAT? Putting these into a data set should allow for macro variable construction via the call symput routine. We could, using the catx function in the data step, assemble these into a list and then run the remainder of the code exactly as we did previously. But it would be silly to put them together only to immediately separate them again. In this case, we will create a set of macro variables for the set of regions, something like this: data _null_; set regions; call symput(cats('region',put(_n_,1.)),strip(region)); We create an indexed set of macro variables, as we have before. This time, the data step record counter, _n_, is used to create the numerical suffix.
A LITTLE BETTER Now a counter could be manually set up with a conditional loop, similar to the iteration from the previous example, but the information is available to tell the program exactly how many times to loop. Have the data step create one more macro variable: The end= variable helps determine when the end of the set is reached... data _null_; set regions end=final; call symput(cats('region',put(_n_,1.)),strip(region)); if final then do; call symput('num',put(_n_,1.)); end;
A LITTLE BETTER Now a counter could be manually set up with a conditional loop, similar to the iteration from the previous example, but the information is available to tell the program exactly how many times to loop. Have the data step create one more macro variable: data _null_; set regions end=final; call symput(cats('region',put(_n_,1.)),strip(region)); if final then do; call symput('num',put(_n_,1.)); end; So the value of the data step counter is now the length of the list.
THE ITERATIVE DO LOOP Since the number of iterations has been determined, conditional iteration is not necessary and an iterative loop can be employed. Macro do loop, iterative: %do var=exp1 %to exp2 %by exp3; %end; SAS statements Requirements and options: exp1, exp2 and exp3 must each evaluate to an integer (usually positive) The %by is optional, the default value is 1 var is referred to as the index variable and is a macro variable The loop iterates for all values from exp1 (inclusive) to exp2, as dictated by exp3 depending on the spacing of these values, there is no guarantee that the index variable will take on the value of exp2
FINISH So following the sort and data step, this loop completes the macro: %do j=1 %to # options nodate pageno=1; ods pdf file="&®ion&j...pdf"; Title "Job Cost Reporting for %sysfunc(propcase(&®ion&j)) Region"; proc report data=mysas.projects nowd; where region eq "&®ion&j"; column pol_type equipmnt personel jobtotal; define pol_type/group 'Pollutant'; define equipmnt/mean 'Avg. Equipment Cost' format=dollar10.; define personel/mean 'Avg. Personnel Cost' format=dollar10.; define jobtotal/mean 'Avg. Total Cost' format=dollar10.; quit; ods pdf close; %end;
FINISH So following the sort and data step, this loop completes the macro: %do j=1 %to # %end; options nodate pageno=1; ods pdf file="&®ion&j...pdf"; Title "Job Cost Reporting for %sysfunc(propcase(&®ion&j)) Region"; proc report data=mysas.projects nowd; quit; ods pdf close; where region eq "&®ion&j"; column pol_type equipmnt personel jobtotal; define pol_type/group 'Pollutant'; define equipmnt/mean 'Avg. Equipment Cost' format=dollar10.; define personel/mean 'Avg. Personnel Cost' format=dollar10.; define jobtotal/mean 'Avg. Total Cost' format=dollar10.; Since j is the chosen do loop index, all references to region are now of the indexed form &®ion&j
FINISH So following the sort and data step, this loop completes the macro: %do j=1 %to # options nodate pageno=1; ods pdf file="&®ion&j...pdf"; Why three dots? Title "Job Cost Reporting for %sysfunc(propcase(&®ion&j)) Region"; proc report data=mysas.projects nowd; where region eq "&®ion&j"; column pol_type equipmnt personel jobtotal; define pol_type/group 'Pollutant'; define equipmnt/mean 'Avg. Equipment Cost' format=dollar10.; define personel/mean 'Avg. Personnel Cost' format=dollar10.; define jobtotal/mean 'Avg. Total Cost' format=dollar10.; quit; ods pdf close; %end;
EXERCISE 1 Modify the previous so the user can choose whether they want reports for each region or each pollution type. The one not chosen should be the grouping variable in the report.
EXERCISE 2 For the data sets in the Monthly Data 4 folder, write a macro that will combine all of these data sets into a single data set (including a variable that indicates where the data originated from.