Calling a global statement inside a loop

0

The other day I was creating some histograms inside a loop in PROC IML. It was difficult for me to determine which histogram was associated with which value of the looping variable. "No problem," I said. "I'll just use a TITLE statement inside the loop so that each histogram has a different title."

Easier said than done.

You see, the TITLE statement requires an argument that is a literal string value. Because the TITLE statement is a "global statement" in SAS and not a statement in the SAS/IML language, the argument cannot be a SAS/IML variable. In other words, the following attempt to set the title inside a loop is not correct:

ods graphics / width=400px;
proc iml;
x = j(50,1);
do mu = 0 to 1;
   call randgen(x, "Normal", mu);   /* x ~ N(mu, 1) */
   msg = "mu = " + strip(char(mu)); /* concatenate strings */
   title msg;                       /* try to form title (DOESN'T WORK!) */ 
   call histogram(x);
end;

You might try to use macro variables to change the title within the loop. For example, you might be tempted to set create a macro variable inside the loop and write the title statement as

   title "mu = &mu";               /* use macro variable (DOESN'T WORK!) */

However, that attempt also fails, for reasons that I have described in a previous article about macro variables and SAS/IML loops.

The solution that I came up with requires using the CALL EXECUTE subroutine in SAS/IML. The EXECUTE subroutine enables you to execute any string that represents a valid SAS or SAS/IML statement. In particular, you can create the string dynamically by using string concatenation operations, and then create the TITLE statement in a similar way. You can use the CALL EXECUTE subroutine to run the TITLE statement for each iteration of the loop. Here's one way to do it:

/* module that executes the TITLE statement on an arbitrary string */
start SetTitle(msg);
   cmd = 'TITLE "' + msg + '";';    /* create TITLE stmt by concatenating */
   call execute(cmd);
finish;
 
x = j(50,1);
do mu = 0 to 1;
   call randgen(x, "Normal", mu);   /* x ~ N(mu, 1) */
   msg = "mu = " + strip(char(mu)); /* concatenate strings */
   run SetTitle(msg);
   call histogram(x);
end;
globalloop

There are a few subtle tricks in this code that are worth explaining:

  • In the SetTitle module, single quotes are used to form the string in the cmd variable. That is because the string itself contains double quotes.
  • The string in the cmd variable contains a semicolon at the end.
  • The EXECUTE subroutine is called from a module. Prior to SAS/IML 13.2, it was not possible to call the EXECUTE subroutine in the main scope of the program. This restriction has been lifted in recent versions of SAS/IML.

Obviously the same technique will use with other statements that require literal strings, including the FOOTNOTE and LIBNAME statements. However, you can use this technique to call SAS/IML statements as well. For example, you can call the MATTRIB statement within a loop to assign matrix attributes to multiple matrices that are enumerated in a list.

Share

About Author

Rick Wicklin

Distinguished Researcher in Computational Statistics

Rick Wicklin, PhD, is a distinguished researcher in computational statistics at SAS and is a principal developer of SAS/IML software. His areas of expertise include computational statistics, simulation, statistical graphics, and modern methods in statistical data analysis. Rick is author of the books Statistical Programming with SAS/IML Software and Simulating Data with SAS.

Leave A Reply

Back to Top