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;
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.