BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
NKormanik
Barite | Level 11

I think this is a 'macro' question.  Have very little experience with that.  Seems a bit overwhelming.

 

Please see the following code:

 

ods html close;
ods html5 path="c:\0_SAS_1" file="i_20201.htm";
proc freq data=sas_1.variable_values_separate_runs
nlevels order=freq; where Indicator = "i_20201"; tables indicator * range / nocum nocol norow
crosslist out=sas_1.i_20201; weight number_weighted_yes / zeros; run; ods html close;

The code above focuses on a variable i_20201.

 

Suppose I have 100 different variables that I'd like to focus on:

 

i_00211
i_00235
i_00249
i_00250
i_00262

...
i_00275
i_00283
i_00290
i_00296

 

So, I have a list of these other variables.  Same program as at the top here.

 

I need to replace i_20201 with each subsequent variable, and run the program again.

 

My hunch is to use a 'macro variable' statement at start of program:

 

%LET xyz = i_20201;

 

And wherever "&xyz." appears in the program, i_20201 will appear replacing it.  (By the way, notice the little period following xyz?  Super important.  Spent hours on that one.)

 

Helpful, sort of.  But not the whole job.  I need to get each of the 100 different variables to be placed there, respectively, for subsequent runs.

 

Is there a relatively easy way of going about this?

 

Thoughts greatly appreciated.

 

Nicholas Kormanik

 

 

 

 

 
 
 
1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

In general the SAS macro language is the way to dynamically generate code.  There are some useful guides you can find with a little search that show how to convert a working program to a macro.  But the steps are simple.  Identify that parts that change.  Replace those with macro variable references.  Then convert that program to macro with the macro variables as the parameters of the macro.  So you have picked XYZ as the name of the macro variable, so let's use that.  You also need to name you macro, so let's call in %RUN_XYZ().

%macro run_xyz(xyz);
ods html close;
ods html5 path="c:\0_SAS_1" file="&xyz..htm";
proc freq data=sas_1.variable_values_separate_runs  nlevels order=freq;
  where Indicator = "&xyz."; 
  tables indicator * range / nocum nocol norow  crosslist out=sas_1.&xyz.;
  weight number_weighted_yes / zeros; 
run; 
ods html close;
%mend run_xyz;

Now to run it for a list of values you will need the list.  You could a separate macro call for each value. Like:

%run_xyz(i_00211)
%run_xyz(i_00235)
%run_xyz(i_00249)

But if you have the list of values in a dataset it is probably easier to let a data step generate those macro calls for you.

data _null_;
  set list;
  call execute(cats('%nrstr(%run_abc)(',indicator,')'));
run;

 

View solution in original post

7 REPLIES 7
PGStats
Opal | Level 21

Why not request something like:

 

proc freq data=sas_1.variable_values_separate_runs  nlevels order=freq;
tables (i_00:) * range / nocum nocol norow  crosslist ;
weight number_weighted_yes / zeros; 
ods output CrossList =sas_1.i_Table;
run; 

edit: Used ODS to get cross list.

PG
NKormanik
Barite | Level 11

@PGStats, have you a link where I could read more about your solution?

 

Like, what in the world, magic.  Coming from you, I'm confident the magic is legit.  But I need to see an additional example or two.  And a bit of explanation.

 

Sort of like... "Covid relief!"  Uhh, yes, but....

 

Additionally, by implication, this matter isn't solved via macro magic?

 

Quickly perusing the Internet, seems you might be suggesting something that is specific to Proc Freq.  "CrossList."

 

I am hoping for a more general fix.  Something along the lines of: Go run program code (any program code) with whatever the current xyz translates to.  Then come back and get the next.  Then the next.

 

The approach I've heretofore been taking was to put the entire program on one single line.  Then copy all lines down (x100, or x1000).  Then change entire columns to the correct values.  Primitive, to you all.  So was hoping for some better approach.

 

 

 
 
 
 
ballardw
Super User

One way is to use call execute. The steps would be to create a data set with a variable holding the value you need to use, one record per value and something like:

data _null_;
   set indicatorlist;
   call execute ('ods html5 path="c:\0_SAS_1" file="');
   call execute( strip(indicator));
   call execute ('.htm";'); 
   call execute ('proc freq data=sas_1.variable_values_separate_runs  nlevels order=freq;
where Indicator = "');
   call execute( strip(indicator));
   call execute ('"; 
     tables indicator * range / nocum nocol norow  crosslist out=sas_1.')
   call execute( strip(indicator));
   call execute (';
     weight number_weighted_yes / zeros; 
     run; 
    ods html close;');
run;

Watch the matching quotes. The boiler plate code extends across several lines in places and the single quotes are so the " don't mess up the alignments.

I use strip(indicator) assuming your variable would be named indicator and some of the places you are using the value you wouldn't want trailing spaces.

Or create a long variable string with CATS such and call execute the variable:

 

data _null_;
   set indicatorlist;
   length longvar $ 300;

   longvar = cats('ods html5 path="c:\0_SAS_1" file="',strip(indicator),'.htm";');
   call execute (longvar);
   longvar = cats('proc freq data=sas_1.variable_values_separate_runs  nlevels order=freq;
   where Indicator = "',strip(indicator),'";');
   call execute (longvar);
   longvar = cats('tables indicator * range / nocum nocol norow  crosslist out=sas_1.',
                  strip(indicator),';');
   call execute (longvar);
   call execute (';
     weight number_weighted_yes / zeros; 
     run; 
    ods html close;');
run;

Or use either approach to write to program file with PUT statements and the %include the result file. This has the advantage of documenting what ran and could be executed again later if the list hasn't changed.

 

 

Tom
Super User Tom
Super User

I do NOT see 100 different variables in your code.  Instead you appear to be asking to use different values of one variable named INDICATOR.

 

In that situation why not just do all 100 at one time instead of looping? 

Either use BY statement or since you are using PROC FREQ just include INDICATOR in the TABLES statement.

Tom
Super User Tom
Super User

In general the SAS macro language is the way to dynamically generate code.  There are some useful guides you can find with a little search that show how to convert a working program to a macro.  But the steps are simple.  Identify that parts that change.  Replace those with macro variable references.  Then convert that program to macro with the macro variables as the parameters of the macro.  So you have picked XYZ as the name of the macro variable, so let's use that.  You also need to name you macro, so let's call in %RUN_XYZ().

%macro run_xyz(xyz);
ods html close;
ods html5 path="c:\0_SAS_1" file="&xyz..htm";
proc freq data=sas_1.variable_values_separate_runs  nlevels order=freq;
  where Indicator = "&xyz."; 
  tables indicator * range / nocum nocol norow  crosslist out=sas_1.&xyz.;
  weight number_weighted_yes / zeros; 
run; 
ods html close;
%mend run_xyz;

Now to run it for a list of values you will need the list.  You could a separate macro call for each value. Like:

%run_xyz(i_00211)
%run_xyz(i_00235)
%run_xyz(i_00249)

But if you have the list of values in a dataset it is probably easier to let a data step generate those macro calls for you.

data _null_;
  set list;
  call execute(cats('%nrstr(%run_abc)(',indicator,')'));
run;

 

NKormanik
Barite | Level 11

All appreciated suggestions.  The first two I'm too unskilled to fully appreciate; maybe some year....

 

@Tom 's suggestion I can better understand.  And I've seen documentation out there that is similar.  Clever idea to convert entire program into a macro.  Haven't tried that yet.

 

The approach I'm going to try to work with is as follows:

 

1)  Create complete program that works with one 'variable' or 'thing'.

 

2)  Search-and-replace (text editor of choice) every instance of the one "thing" that worked with "&xyz.", not including the quotation marks.  Save this file to a particular location, such as C:\1\program.sas.

 

3)  Create a secondary file, such as "run_xyz.sas".  Contents of the secondary file:

%let xyz = i_00211   ; %include "C:\1\program.sas";
%let xyz = i_00235   ; %include "C:\1\program.sas";
%let xyz = i_00249   ; %include "C:\1\program.sas";
%let xyz = i_00250   ; %include "C:\1\program.sas";
%let xyz = i_00262   ; %include "C:\1\program.sas";
%let xyz = ...       ; %include "C:\1\program.sas";
%let xyz = i_00275   ; %include "C:\1\program.sas";
%let xyz = i_00283   ; %include "C:\1\program.sas";
%let xyz = i_00290   ; %include "C:\1\program.sas";
%let xyz = i_00296   ; %include "C:\1\program.sas";

4)  Run "run_xyz.sas".

 

So far seems to be working.  Any suggestions?

 

Thanks again to all here....

 

 

 

 

 

Kurt_Bremser
Super User

From your code, you are not dealing with variables, but with values.

SAS procedures (in conjunction with ODS) allow to create separate pages for each value in one procedure call.

If you post example data and an example for the expected report layout, we can come up with a proper solution.

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 7 replies
  • 624 views
  • 5 likes
  • 5 in conversation