BookmarkSubscribeRSS Feed
aaronh
Quartz | Level 8

As a simplified example of what I am currently working on...

 

Suppose I want to compare the performance of Roger Federer, Novak Djokovic, and Rafa Nadal in several Grand Slam tournaments last year. And suppose that I have a few datasets that contain each player's performance (such as 1st and 2nd serve win rate) and these datasets are named in the following manner:

 

    player_tournament_round  (e.g  Federer_USOpen_round1, Nadal_Wimbledon_round4)

 

Suppose that I am only interested in the first 2 rounds of US Open, rounds 3-5 of Wimbledon, and first 4 rounds of Australian Open (assuming that the players attended all of these matches).

 

So I already have a macro called 'input' that reads in these datasets and performs some tweaks, and it is specified as:

   

  %macro input(player, tour, round);

 

In SAS, I invoked the macro as follows:

 

  %input(Federer, USOpen, round1);

  %input(Federer, USOpen, round2);

  %input(Federer, Wimbledon, round3);

  %input(Federer, Wimbledon, round4);

  %input(Federer, Wimbledon, round5);

  %input(Federer, AUOpen, round1);

  %input(Federer, AUOpen, round2);

  %input(Federer, AUOpen, round3);

  %input(Federer, AUOpen, round4);

 

  %input(Djokovic, USOpen, round1);

  %input(Djokovic, USOpen, round2);

  %input(Djokovic, Wimbledon, round3);

  %input(Djokovic, Wimbledon, round4);

  %input(Djokovic, Wimbledon, round5);

  %input(Djokovic, AUOpen, round1);

  %input(Djokovic, AUOpen, round2);

  %input(Djokovic, AUOpen, round3);

  %input(Djokovic, AUOpen, round4);

 

  %input(Nadal, USOpen, round1);

  %input(Nadal, USOpen, round2);

  %input(Nadal, Wimbledon, round3);

  %input(Nadal, Wimbledon, round4);

  %input(Nadal, Wimbledon, round5);

  %input(Nadal, AUOpen, round1);

  %input(Nadal, AUOpen, round2);

  %input(Nadal, AUOpen, round3);

  %input(Nadal, AUOpen, round4);

 

--------------------------------------------------

As you can see, the macro invocation becomes quite redundant, because, for each player, I always need to enter rounds 1-2 for US Open, rounds 3-5 for Wimbeldon, and rounds 1-4 for AU Open.

 

So my question is: how do I tell SAS to automatically do:

  rounds 1-2 if &tour. = USOpen

  rounds 3-5 if &tour. = Wimbledon

  rounds 1-4 if &tour. = AUOpen

 

I have some thoughts, such as an array (do loop) or nested macro, but I have not been able to code it. Some help will be much appreciated!

 

 

7 REPLIES 7
Tom
Super User Tom
Super User

In a sense you are asking two questions here.

The first (easy) question is how to default a value of a parameter. The answer is to make your macro smarter.

Have it check if the ROUND value is supplied and if not set it based on your logic.

%macro input(player, tour, round);
%if 0=%length(&round) %then %do;
  %if "&tour" = "USOpen" %then %let round=round1 round2;
  %else %if "&tour" = "Wimbledon" %then %let round=round3 round4;
...
%end;
....
%mend input;

The second is how to make the macro process more than one round at a time.  I hinted at a way in answer to first question.  You could make the macro accept multiple values in the ROUND parameter.  Then adjust the coding appropriately.  For details we would need to see how it works.  Frequently it is just a matter of using the value with IN operator in a WHERE clause.

where round in %qlist(&round)

https://github.com/sasutils/macros/blob/master/qlist.sas

aaronh
Quartz | Level 8

Thank you for your help Tom.

 

My macro is actually quite lengthy, but to give you some persepctive:

 

 

%macro input(player, tour, round)/minoperator;

proc contents data=&player._&tour._&round. varnum;
run;

%if &player. in (Federer Nadal) 
   %then %do;
   data &player_&tour._&round._2;
   set &player_&tour._&round. (keep = A B C D);
   if ... then ...;
   run;
   %end;

   %else %do;
   data &player_&tour._&round._2;
   set &player_&tour._&round.;
   if ... then ...;
   run;
   %end;

%mend input;

 

 

I guess one of the challenges here is that &round. is actually used as part of the table names in this process.

 

Sorry if I am not describing my question here the best I can, as this topic is only an example of what I am actually working on right now. But I am hoping that the 2.0 version of the macro will look like:

 

%input2(Federer, USOpen);

%input2(Federer, Wimbledon);

%input2(Federer, AUOpen);

 

%input2(Djokovic, USOpen);

%input2(Djokovic, Wimbledon);

%input2(Djokovic, AUOpen);

 

%input2(Nadal, USOpen);

%input2(Nadal, Wimbledon);

%input2(Nadal, AUOpen);

 

but still achieves the same exact things as what I currently have.

 

PaigeMiller
Diamond | Level 26

@aaronh wrote:

Thank you for your help Tom.

 

My macro is actually quite lengthy, but to give you some persepctive:

 

 

%macro input(player, tour, round)/minoperator;

proc contents data=&player._&tour._&round. varnum;
run;

%if &player. in (Federer Nadal) 
   %then %do;
   data &player_&tour._&round._2;
   set &player_&tour._&round. (keep = A B C D);
   if ... then ...;
   run;
   %end;

   %else %do;
   data &player_&tour._&round._2;
   set &player_&tour._&round.;
   if ... then ...;
   run;
   %end;

%mend input;

 

 

I guess one of the challenges here is that &round. is actually used as part of the table names in this process.

 

Sorry if I am not describing my question here the best I can, as this topic is only an example of what I am actually working on right now. But I am hoping that the 2.0 version of the macro will look like:

 

%input2(Federer, USOpen);

%input2(Federer, Wimbledon);

%input2(Federer, AUOpen);

 

%input2(Djokovic, USOpen);

%input2(Djokovic, Wimbledon);

%input2(Djokovic, AUOpen);

 

%input2(Nadal, USOpen);

%input2(Nadal, Wimbledon);

%input2(Nadal, AUOpen);

 

but still achieves the same exact things as what I currently have.

 


Yes and so inside the macro, for Federer and USOpen, the macro knows which rounds to analyze; and so on. Then you wouldn't have to call the macro so many times. Better yet, you could possible rewrite the macro so that you only have to call it once, and the logic inside the macro will know what to do

 

%input2(players=Nadal Federer Djokovic,tournaments=USOpen Wimbledon AUOpen)

 

But all of this depends on exactly what you are doing and how you want to do it and how much programming you want to do.

 

 

--
Paige Miller
PaigeMiller
Diamond | Level 26

Another thought is to put all of your combinations into a text file, then have the macro read each record of the text file, and do it's thing. The advantage of this is now you don't have to program the combinations, and so if the combinations change over time, you don't have to change the code, you only have to change the text file.

 

Said text file might look like this (obviously it would have many more lines).

 

federer,usopen,round3
federer,wimbledon,round4

It all depends on your needs, and how much effort you want to put into this. If the inputs are going to change over time, this might be a better way to go.

--
Paige Miller
Tom
Super User Tom
Super User

Another way to deal with a list of values is to loop over them and pull out one at a time.

%let round_list=round1 round2;
%do round_no=1 %to %sysfunc(countw(&round_list,%str( )));
  %let round=%scan(&round_list,&round_no,%str( ));
   ..... &round. ....
%end;
aaronh
Quartz | Level 8

I found a way of achieving this a while back, so I am posting my solution here in case someone else encounters the same problem as I had in the future...

 

As a said earlier, I already have a SAS macro called 'input' that reads in the datasets, but it gets quite redundant for each grand slam tournament (because I always only want the 1st two matches in US Open, 3rd-5th matches in Wimbledon, and 1st four matches in Australian Open). As such, I am creating a 2nd SAS macro that uses my 'input' macro as follows:

 

%macro readin(player, tour);
  %if &tour.=USOpen %then %do;
     %input(player, tour, round1);
     %input(player, tour, round2);
  %end;

  %else %if &tour.=Wimbledon %then %do;
     %input(player, tour, round3);
     %input(player, tour, round4);
     %input(player, tour, round5);
  %end;

  %else %if &tour.=AUOpen %then %do;
     %input(player, tour, round1);
     %input(player, tour, round2);
     %input(player, tour, round3);
     %input(player, tour, round4);
%end; %else %do; %put Undefined input for grand slam tournament; abort cancel; %end;

%mend readin;

 

As such, I can achieve what I want with 9 lines of code (instead of the previous 27 lines):

 

%readin(Federer, USOpen);
%readin(Federer, Wimbledon);
%readin(Federer, AUOpen);

%readin(Djokovic, USOpen);
%readin(Djokovic, Wimbledon);
%readin(Djokovic, AUOpen);

%readin(Nadal, USOpen);
%readin(Nadal, Wimbledon);
%readin(Nadal, AUOpen);

Here, the results from the macro 'readin' are the exact same as from the macro 'input', but 'readin' saves 18 lines of macro invocation, and reduces the potential of typing/copying and pasting errors... I've heard that nesting macros can be bad from a performance stand point, but in my case, I think it's well worth it. Hope this helps (:

 

PaigeMiller
Diamond | Level 26

@aaronh wrote:

I've heard that nesting macros can be bad from a performance stand point, but in my case, I think it's well worth it.


Nesting the macro definitions is not recommended.

 

Example:

%macro one;
    data abc;
        ...
    run;
    %macro two;
         data def;
              ...
         run;
     %mend;
%mend;

However what your code does is nesting of macro calls, and that is fine, there are no problems when the macros themselves work.

 

--
Paige Miller

hackathon24-white-horiz.png

2025 SAS Hackathon: There is still time!

Good news: We've extended SAS Hackathon registration until Sept. 12, so you still have time to be part of our biggest event yet – our five-year anniversary!

Register Now

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 7 replies
  • 1538 views
  • 5 likes
  • 3 in conversation