Learning Macros
With just a few new commands to learn you may generate thousands lines of syntax!
Index
- The Basics
- Macros and The Command Terminator
- Just (!) a string parser
- Fully commented example of a macro (obtain n independent samples of size m)
- Replacing a character in many string variables
- Changing storage type for multiple variables (several examples, from simple to more complex)
- Version 1: to change the format (the name remains the same) of a variable from string to string, numeric to string or string to numeric
- Version 2: same as above but doing 5-10 variables in the same macro call
- Version 3: same as above but doing it for ALL numeric or string variables between 2 given variables in the data file.
- Doing arithmetic with macro variables (for advanced users)
- Another fully commented macro (print bar charts excluding categories with less than n cases)
Let me know if you have suggestions for subjects to be covered here or if a given area is not clear.
The Basics
Note that you have to be familiar with syntax in order to be in position to understand macros.
A macro is similar to a function. First, you define it, then you have to 'call it'. The macro needs to be defined only once per SPSS Statistics "session". A session begins when you start SPSS Statistics and ends when you shut it down. Thus if you start SPSS Statistics , define a macro then work with many different data files, the macro still exists and can be called at any time without having to be redefined. However, if you shut down SPSS Statistics and start it again, you have to redefine the macro (that is, execute the code between the DEFINE
and !ENDDEFINE
commands) before being able to call it.
For short, we'll further name here SPSS Statistics simply as SPSS.
Macro definitions may be placed anywhere in the syntax file because they get "preferential treatment" in the sense that they are processed (that is they are "defined") before the rest of the syntax file is processed.
Here is a very simple example of a macro:
DEFINE !stats (myvars=!CMDEND) FREQUENCY VARIABLES= !myvars /ORDER= ANALYSIS. !ENDDEFINE.
You would call the macro using a line such as
!stats myvars = age sex status.
assuming age sex and status are the variables for which you want the frequency information.
The DEFINE
statement (the first line of the macro definition) tells you that the name of the macro is !stats
. That statement also tells you that when you call the macro, you have to assign the variables of interest to myvars
. This has been done in the macro call where we have myvars=age sex status
.
It is important to note that although the macro variable is named myvars
in the DEFINE
line, it must be referred to as "!myvars" within the macro.
Once the macro is defined, the line
!stats myvars = age sex status.
has the same effect as the line
FREQUENCY VARIABLES= age sex status /ORDER= ANALYSIS.
You might be thinking "What's the big deal?". The big deal is that macros can do much more complex tasks that a simple Frequency; they could be doing things which would otherwise require hundreds of lines of syntax and hours of work. With a macro, you need only one line to call and execute all those lines. There are over 140 macros in this site. The best way to find all of them is to search for "!ENDDEFINE
" using the Search tool which is at the home page of this site. (Worth to check also a separate page on this site, "Kirill's SPSS macros", with a lot of advanced, thoroughly developed macros by Kirill Orlov.)
Macros and The Command Terminator
The "Command Terminator" is the period which must end each syntax command in "interactive mode". (There exists also "batch mode" possible to use with INSERT command, but let us not discuss it here. The "interactive mode" of submitting syntax for running is the default in SPSS, even with INSERT, and the strong recommendation is to always end syntax commands with the period.)
However the sequence beginning with DEFINE
and ending with !ENDDEFINE
- called the macro "body" - is just like one syntax command. It properly ends with a period after !ENDDEFINE
. But it is not necessary (and even not recommended) to insert a period at the end of each macro language statement within the body. In some cases, inserting a period after a macro statement will create a bug! (for an example see this macro) Doing so could confuse the processor. Therefore the general rule is: do not terminate macro language statements with a period. For instance, !DO !var=1 !TO !nbfiles
is a macro statement and does not need a command terminator.
Of course macros often contain syntax command, for instance the following line needs to end with a period:
SAVE OUTFILE=!QUOTE(!CONCAT("c:\Temp\new file ",!var,".sav")).
because SAVE... is a regular syntax command (which happens to be inside a macro body) and not a macro statement, and syntax commands need to end with a period. (This SAVE contains a macro expression, in a form of !QUOTE macro function inside it, true, but SAVE is a syntax command.)
Just (!) a string parser
The fundamental thing to understand with respect to Macros is that macro language is just a "string parser". By this I mean that macro's magic is entirely performed before the first data case has been processed by a given macro.
Once you understand this, it is easy to see that a statement such as
!IF (!age !GT age) !THEN ... !IFEND
does not make sense. It does not make sense because age is a variable whose value (normally) varies by case; but since the macro is "expanded" before the first case is read, the macro parser does not have a clue whether the !IF --- !IFEND
condition is satisfied or not. The macro knows nothing about your data values.
On the other hand the following macro line in a macro body is acceptable
COMPUTE years2 = 65 - !age.
since the macro expansion can replace !age by the value (some constant, scalar, or some variable name) given in the macro call or defined within the macro by a !LET
statement. The replacement is not conditional on information coming from individual data cases.
Fully Commented Example of a Macro
I list a macro then describe the purpose of each line of code. Macro was created as a solution for the post "How can I obtain n independent random samples of size m from the same source file?" to SPSSX-L. Solutions was posted to the list by rlevesque@videotron.ca on 2001/04/14.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 1 PRESERVE. 2 SET MPRINT=no. 3 *///////////////////////////////. 4 DEFINE !getsamp (nbcases=!TOKENS(1) /nbfiles=!TOKENS(1) ) 5 !DO !var=1 !TO !nbfiles 6 GET FILE='C:\Program Files\SPSS\GSS93 subset.sav'. 7 COMPUTE draw=UNIFORM(1). 8 COMPUTE dummy=1. 9 RANK VARIABLES=draw BY dummy /RANK INTO rank1. 10 SELECT IF rank1 LE !nbcases. 11 SAVE OUTFILE=!QUOTE(!CONCAT("c:\Temp\new file ",!var,".sav")). 12 !DOEND 13 !ENDDEFINE. 14 *///////////////////////////////. 15 *Next line starts the whole process. 16 SET MPRINT=yes. 17 !getsamp nbcases=300 nbfiles=4. 18 RESTORE. |
The macro definition starts at line 4 and ends at line 13 (contents between these lines is the macro body). The macro is called (run for execution) at line 17. When the macro is called, it is expanded, and the regular SPSS syntax resulting from the expansion, is run.
Line # and Comments on the line:
- This command works in conjunction with the command
RESTORE
in line 18. From the Syntax Guide we have:PRESERVE
stores currentSET
specifications that can later be restored by theRESTORE
command.PRESERVE
andRESTORE
are especially useful with the macro facility.PRESERVE…RESTORE
sequences can be nested up to five levels. - I use the
SET MPRINT
command all the time when I write or debug a macro. For greater readability of the Output Window, it is preferable to set it toNO
before the macro definition starts and to set it toYES
(see line 16) before calling the macro. - I like to enclose my macros between special characters which I use just for this purpose. See line 3 and 14. This allows me to locate macros faster in long syntax files.
- The beginning of the macro. The name of the macro is !getsamp; I like to start macro names with ! this helps to quickly identify macro names in ordinary syntax: starting macro names with the ! is a good practice. The macro requires 2 arguments when called:
nbcases
andnbfiles
. Thus ifnbcases=500
andnbfiles=40
, the macro will generate 40 independent random files of 500 cases each. Each sample will be saved in its own file. - This line works in conjunction with line 12. It is the cycle (looping) structure. The macro variable !var will take all values from 1 to the value of !nbfiles which was given in the macro call. Warning: Note that the name of the arguments in line 4 do not start with an "!" however when reference is made within the macro to these parameters, it is necessary to add a "!" at the beginning of the parameter name.
- This loads the file in memory. As written, the macro needs to be changed when the initial population file changes. It would be easy to modify the macro so that the name and path of the file would be given as an argument in the macro call. There would then be 3 arguments instead of 2. One advantage of giving the file name in the macro call is that the macro can then be loaded in memory using an
INSERT
command. - This line defines a new variable named draw and assigns a random value between 0 and 1 to each case in in the file.
- This defines a new variable named dummy which equals 1 for all cases in the file. This variable will be needed in line 9.
- This ranks each value of draw in increasing order. For each case, the rank of the value of draw is contained in the variable rank1. Thus the smallest value of draw in the file gets the rank1=1. The second smallest value gets rank1=2.
- It is clear that each case of the original file has an equal chance of getting the smallest, second smallest, etc. value of draw. Thus when, in line 10, we select the cases for which the rank1 has a value between 1 and
!nbcases
(in other words, the!nbcases
cases which have the smallest value of draw), we are selecting a random sample of the initial population. - This saves the random sample in a file. When
!var1
equals1
, the name of the file is "new file 1.sav", when!var2=2
, the name is "new file 2.sav" and so on up to 300. See the Syntax Reference Guide for explanations of!QUOTE
and!CONCAT
macro functions. - The end of the macro loop.
- The end of the macro definition.
- See comments on line 3.
- This line is just a comment.
- Once a macro is working, you may replace the "yes" by a "no" in order to reduce the number of lines in the LOG section of the OUTPUT Window.
- The macro call
!getsamp nbcases=300 nbfiles=4.
causes the macro to get 4 random files of 300 cases each. Similarly!getsamp nbcases=300 nbfiles=1000.
would generate 1,000 random files. The macro definition (10 lines) plus that one line of macro call are replacing about 10,000 lines of syntax which would be required without a macro!! It should be clear to anybody that macros are very useful.
Note that if the only thing required were to calculate the Mean, Standard Deviation or some other statistics for each of the sample files, then these values could be saved to a single result file; there would be only one file created by the macro.
The macro definition (lines 4 through 13) has to be run only once in SPSS session. Of course, if you modify something in the macro body, you will have to rerun ("reload") the macro definition. Tip: If you thrice-click by your mouse somewhere inside the macro body, all the macro definition from DEFINE
till !ENDDEFINE
gets selected. This is very convenient in order to run a macro definition.
Replacing a character in many string variables
In String Manipulation Tutorial the following syntax is used to replace "." by ",":
LOOP IF CHAR.INDEX(name2,".")>0. - COMPUTE SUBSTR(name2,CHAR.INDEX(name2,"."),1)=",". END LOOP.
Using the same technique for 100 variables would require 300 lines of code! The following circumstances are presented and the solutions are given.
- Example 1. Assume variables have related names but are not necessarily consecutive in the data file (for instance, var1, var2, var3,… or any other characters instead of "var")
- Example 2 Assume variables have unrelated names but are consecutive in the data file (for instance vara, name, address,…, product are consecutive in the data file)
- Example 3. Assume variables have unrelated names but are non consecutive in the data file, however all string variables between the first variable (for instance "vara" in 2 above) and the last variable ("product" in 2 above) need to be processed.
- Example 4 . Assume variable have unrelated names and are not consecutive in the file. In addition, some of the string variables between the first variable (for instance "vara" in 2 above) and the last variable ("product" in 2 above) must not be processed.
The file Change some characters inside many string variables.sps contains the 4 examples given below.
Replace Characters Example 1
Variables have related names but are not necessarily consecutive in the data file (for instance, the names are var1, var2, var3, … or any other characters instead of "var")
* Create a data file to work with. INPUT PROGRAM. VECTOR var(100A8). LOOP id=1 TO 3. LOOP cnt=1 TO 100. COMPUTE var(cnt)="txt.abc". END LOOP. END CASE. END LOOP. END FILE. END INPUT PROGRAM. EXECUTE. * The following approach using a loop does NOT work because SUBSTR needs an actual variable name. * It does not recognize the vector v(cnt) as a variable name. VECTOR v=var1 TO var100. LOOP cnt=1 TO 100. LOOP IF CHAR.INDEX(v(cnt),".")>0. COMPUTE SUBSTR(v(cnt),CHAR.INDEX(v(cnt),"."),1)=",". END LOOP. END LOOP. EXECUTE. * Using DO REPEAT does work. DO REPEAT v=var1 TO var100. LOOP IF CHAR.INDEX(v,".")>0. COMPUTE SUBSTR(v,CHAR.INDEX(v,"."),1)=",". END LOOP. END REPEAT. EXECUTE. * The following macro solution also works. SET MPRINT=no. */////////////////////. DEFINE !replace(vname=!TOKENS(1) /nbvars=!TOKENS(1) /oldchar=!TOKENS(1) /newchar=!TOKENS(1)) * To replace a character in many string variables. !DO !cnt=1 !TO !nbvars LOOP IF CHAR.INDEX(!CONCAT(!vname,!cnt),!oldchar)>0. COMPUTE SUBSTR(!CONCAT(!vname,!cnt),CHAR.INDEX(!CONCAT(!vname,!cnt),!oldchar),1) =!newchar. END LOOP. !DOEND EXECUTE. !ENDDEFINE. */////////////////////. SET MPRINT=yes. * The macro is called as follows. !replace vname=var nbvars=100 oldchar='.' newchar=','. * The DO REPEAT solution is easier than the above macro solution, however, one has to remember that procedures are not allowed inside a DO REPEAT structure, the Macro solution is thus more general.
Replace Characters Example 2
Assume variables have unrelated names but are consecutive in the data file (for instance vara, name, address, …, product are consecutive in the data file)
* The most convenient way to handle this situation is when one has simply to give to a macro the name of the first and last variables which have to be processed. * One of the macro Gems (see "Define list of variables between two variables.sps") can then be used. * Create a data file. INPUT PROGRAM. STRING strone text34 str2 beta gamma alpha (A8). LOOP id=1 TO 3. DO REPEAT var=strone text34 str2 beta gamma alpha. COMPUTE var="txt.ab.c". END REPEAT PRINT. END CASE. END LOOP. END FILE. END INPUT PROGRAM. EXECUTE. SAVE OUTFILE='c:\temp\testdata.sav'. * Suppose we want to process variables between text34 and gamma inclusively. This solution assumes we have hundreds of variables between these 2 variables. It is not convenient to list them manually. * Note however that the following solution can also be used when you know that all variables after a given variables need to be processed but you do not know ahead of time the name of the last variable, in that case you simply create a new string variable and use that name as the "ending variable". * Note: when you save the macro Gem in your macro folder, keep ONLY the macro definition, in other words, delete the data definition and the example of macro call, INSERT FILE='c:\Program Files\SPSS\macros\DefineListOfVariablesBetweenTwoVariables.sps'. * Call the macro Gem to define a macro which gives the list of all variables between the 2 given variables. !DefList var1=text34 var2=gamma fname='c:\temp\testdata.sav'. * The macro is called as follows. SET MPRINT=yes. !rep_chr oldchar='.' newchar=',' vnames=!list1. * See Example 4 below for the definition of the macro !rep_chr.
Replace Characters Example 3
Assume variables have unrelated names but are non consecutive in the data file, however all string variables between the first variable (for instance "vara" in 2 above) and the last variable ("product" in 2 above) need to be processed.
* The same solution as in EXAMPLE 2 can be used. * Create a data file. INPUT PROGRAM. STRING strone text34 str2 beta gamma (A8). LOOP id=1 TO 3. DO REPEAT var=strone text34 str2 beta gamma. COMPUTE var="txt.ab.c". END REPEAT PRINT. END CASE. END LOOP. END FILE. END INPUT PROGRAM. EXECUTE. * create a numeric variable between the first and last string variables. COMPUTE numvar=2. STRING alpha(A8). COMPUTE alpha="txt.a.b.c". SAVE OUTFILE='c:\temp\testdata.sav'. INSERT FILE='c:\Program Files\SPSS\macros\DefineListOfVariablesBetweenTwoVariables.SPS'. * Call the macro Gem to define a macro which gives the list of all variables between the 2 given variables. !DefList var1=text34 var2=alpha fname='c:\temp\testdata.sav'. * The macro is called as follows. SET MPRINT=yes. !rep_chr oldchar='.' newchar=',' vnames=!list1. * Note that *errors* will occur when the macro attempts to modify the numeric variables (variable numvar), as a result of that error, the command will not be executed. However, we do not care since we did not intend to modify numeric variables in any event. * See Example 4 below for the definition of the macro !rep_chr.
Replace Characters Example 4
Assume variable names have unrelated names and are not located consecutively in the data file. Assume further that some of the string variables between the first variable (for instance "vara" in 2 above) and the last variable ("product" in 2 above) must be processed but some other string variables between the first and last variable must not be processed
* In this case, the names of each string variable which needs to be processed must be explicitly given to the macro. INPUT PROGRAM. STRING strone text34 str2 name alpha (A8). LOOP id=1 TO 3. DO REPEAT var=strone text34 str2 name alpha. COMPUTE var="txt.ab.c". END REPEAT PRINT. END CASE. END LOOP. END FILE. END INPUT PROGRAM. EXECUTE. * A macro can be used. SET MPRINT=no. */////////////////////. DEFINE !rep_chr(oldchar=!TOKENS(1) /newchar=!TOKENS(1) /vnames=!CMDEND) /* To replace a character in many string variables */ !DO !vname !IN (!vnames) LOOP IF CHAR.INDEX(!vname,!oldchar)>0. COMPUTE SUBSTR(!vname,CHAR.INDEX(!vname,!oldchar),1)=!newchar. END LOOP. !DOEND EXECUTE. !ENDDEFINE. */////////////////////. * Assume that for variables text34 str2 alpha we want to replace all dots by commas. (The variables are not consecutive in the file.) * The macro is called as follows. SET MPRINT=yes. !rep_chr oldchar='.' newchar=',' vnames=text34 str2 alpha. * Of course if you wanted to replace occurences of 'a' by 'b' you would call the macro as follows. !rep_chr oldchar='a' newchar='b' vnames=text34 str2 alpha.
Changing storage type for multiple variables (several examples, from simple to more complex)
Version 1: to change the format (the name remains the same) of a variable from string to string, numeric to string or string to numeric
* This macro is useful when you have to change the format from * string to string (ss) * numeric to string (ns) or * string to numeric (sn) * while retaining the original variable name. * rlevesque@videotron.ca 2001/05/23. */////////////////////. DEFINE !convert (vname=!TOKENS(1) /nformat=!TOKENS(1) /type=!TOKENS(1)) /* where vname= the name of the variable to change format*/. * nformat = the new format. * type of change = ss (string to string), ns or sn where n=numeric and s=string. !IF (!type='ss') !THEN STRING temp1234(!nformat). COMPUTE temp1234=!vname. !IFEND !IF (!type='ns') !THEN STRING temp1234(!nformat). COMPUTE temp1234=LTRIM(STRING(!vname,F18.0)). !IFEND !IF (!type='sn') !THEN COMPUTE temp1234=NUMBER(!vname,F18.0). FORMAT temp1234(!nformat). !IFEND MATCH FILES FILE=* /DROP=!vname. RENAME VARIABLE (temp1234=!vname). !ENDDEFINE. */////////////////////. * Create a dummy data file to illustrate the use of the macro. DATA LIST LIST /var1(A8) var2(A8) var3(F8.0). BEGIN DATA abcdefg 254 235 adadad 128 265 END DATA. LIST. * Example 1. !convert vname=var1 nformat=A5 type=ss. * The above command converts variable var1 from format (A8) to format (A5) * Example 2. !convert vname=var2 nformat=F8.2 type=sn. * The above command converts variable var2 from format (A8) to format (F8.2). * (Of course if the string contains an invalid number, the resulting numeric variable will be missing). * Example 3. !convert vname=var3 nformat=A8 type=ns. * The above command converts numeric var3 from format (F8.2) to format (A8).
Version 2: same as above but doing 5-10 variables in the same macro call, a generalization
Suppose we often have to change the format of 5 to 10 variables and that the same type (ss sn or ns) is involved. We now change the macro so that only one macro call is required to do all 5–10 variables.
SET MPRINT=yes. */////////////////////. DEFINE !conver2 (type=!TOKENS(1) /cformat=!TOKENS(1) /nformat=!TOKENS(1) /vnames=!CMDEND) /* where. * type = type of change ss (string to string) ns or sn where * n=numeric and s=string. * cformat = the conversion format (only needed when type=ns). * nformat = the new format. * vnames= the names of the variables to change format. !DO !vname !IN (!vnames) !IF (!type='ss') !THEN STRING temp1234(!nformat). COMPUTE temp1234=!vname. !IFEND !IF (!type='ns') !THEN STRING temp1234(!nformat). COMPUTE temp1234=LTRIM(STRING(!vname,!cformat)). !IFEND !IF (!type='sn') !THEN COMPUTE temp1234=NUMBER(!vname,!nformat). FORMAT temp1234(!nformat). !IFEND MATCH FILES FILE=* /DROP=!vname. RENAME VARIABLE (temp1234=!vname). !DOEND !ENDDEFINE. */////////////////////.
* Testing the macro (only 4 variables are used instead of 5-10). DATA LIST LIST /var1(A8) vnum2(A8) varx(A8) vary(A8). BEGIN DATA 1235.23 254.13 235.00 6532.20 53261.32 128.09 265.85 2591.99 END DATA. LIST. * Change the format from 2 to 3 decimal places. !conver2 type=sn nformat=F8.3 vnames=var1 vnum2 varx vary.
Here is the macro expansion of the above macro call.
199 M> 200 M> COMPUTE TEMP1234=NUMBER( var1 , F8.3 ). 201 M> FORMAT TEMP1234( F8.3 ). 202 M> 203 M> MATCH FILES FILE=* /DROP= var1. 204 M> RENAME VARIABLE (TEMP1234= var1 ). 205 M> 206 M> 207 M> COMPUTE TEMP1234=NUMBER( vnum2 , F8.3 ). 208 M> FORMAT TEMP1234( F8.3 ). 209 M> 210 M> MATCH FILES FILE=* /DROP= vnum2. 211 M> RENAME VARIABLE (TEMP1234= vnum2 ). 212 M> 213 M> 214 M> COMPUTE TEMP1234=NUMBER( varx , F8.3 ). 215 M> FORMAT TEMP1234( F8.3 ). 216 M> 217 M> MATCH FILES FILE=* /DROP= varx. 218 M> RENAME VARIABLE (TEMP1234= varx ). 219 M> 220 M> 221 M> COMPUTE TEMP1234=NUMBER( vary , F8.3 ). 222 M> FORMAT TEMP1234( F8.3 ). 223 M> 224 M> MATCH FILES FILE=* /DROP= vary. 225 M> RENAME VARIABLE (TEMP1234= vary ). 226 M> . 227 M>
* Convert the result back to string (but with only one decimal place).
!conver2 type=ns cformat=F8.1 nformat=A8 vnames=var1 vnum2 varx vary.
Here is the macro expansion of the above macro call.
233 M> 234 M> STRING TEMP1234( A8 ). 235 M> COMPUTE TEMP1234=LTRIM(STRING( var1 , F8.1 )). 236 M> 237 M> 238 M> MATCH FILES FILE=* /DROP= var1. 239 M> RENAME VARIABLE (TEMP1234= var1 ). 240 M> 241 M> STRING TEMP1234( A8 ). 242 M> COMPUTE TEMP1234=LTRIM(STRING( vnum2 , F8.1 )). 243 M> 244 M> 245 M> MATCH FILES FILE=* /DROP= vnum2. 246 M> RENAME VARIABLE (TEMP1234= vnum2 ). 247 M> 248 M> STRING TEMP1234( A8 ). 249 M> COMPUTE TEMP1234=LTRIM(STRING( varx , F8.1 )). 250 M> 251 M> 252 M> MATCH FILES FILE=* /DROP= varx. 253 M> RENAME VARIABLE (TEMP1234= varx ). 254 M> 255 M> STRING TEMP1234( A8 ). 256 M> COMPUTE TEMP1234=LTRIM(STRING( vary , F8.1 )). 257 M> 258 M> 259 M> MATCH FILES FILE=* /DROP= vary. 260 M> RENAME VARIABLE (TEMP1234= vary ). 261 M> .
Version 3: same as above but doing it for ALL numeric or string variables between 2 given variables in the data file
This is actually the same macro as version 2 but here we use it jointly with one of the Macro Gems. Suppose we often have to change the format of 600 variables and that the same type (ss sn or ns) is involved.
!DEfList var1=g var2=k fname="c:/temp/mydata.sav". * The macro !DefList (for Define List) can be found here. * The above macro call defines a macro named !list1 whose purpose is to list the names * of all the variables between var1 and var2 in the file c:/temp/mydata.sav * This list is then used as one of the parameter in the macro call of conver2 as follows. !conver2 type=ns cformat=F8.1 nformat=A8 vnames=!list1.
The above 2 macros will convert all variables between g and k (they are all assumed to be numeric) to a string. The names of the variables will remain the same (only the format will change).
Note: If only some variables between variables g and k were numeric we could use the macro !vartype (also located in Macro Gems) to obtain the list of only numeric (or only string) variables. We would then use the correct variable list as the argument of the !conver2 macro call.
Doing arithmetic with macro variables (for advanced users)
It is not possible to (directly) do arithmetic with macro variables. For instance, given the following 2 macro statements:
!LET !a=1. !LET !b=2.
the following is not a valid command:
!LET !c=!a + !b.
The following is an indirect method of obtaining the sum of !a
and !b
.
!LET !c=!LENGTH(!CONCAT(!BLANK(!a),!BLANK(!b)).
This is not a very friendly method… Try to do a division (or calculate the log of !b) with a similar method!
Suppose we need the macro variable !myvar to take on "complex" values such as the ln(2), ln(3) and ln(4). Any number of values and any other formulas could be handled, but I will use these 3 to illustrate the method. The trick is to have SPSS a) calculate the numeric values and then b) write the syntax which will subsequently be run.
SET MPRINT=yes. *///////////////////////////////////. DEFINE !doit(nbval=!TOKENS(1)) INPUT PROGRAM. LOOP cnt=1 TO !nbval. COMPUTE myval=LN(cnt). END CASE. END LOOP. END FILE. END INPUT PROGRAM. * Next line assumes you need 5 digits. FORMAT myval(F8.5). * Define the macro variable. DO IF cnt=1. WRITE OUTFILE='c:\temp\macro var.sps' /"DEFINE !values()" /myval. ELSE IF cnt<!nbval. WRITE OUTFILE='c:\temp\macro var.sps' /myval. ELSE. WRITE OUTFILE='c:\temp\macro var.sps' /myval /"!ENDDEFINE.". END IF. EXECUTE. * Run the syntax to define the variable. INSERT FILE='c:\temp\macro var.sps'. EXECUTE. !ENDDEFINE. *///////////////////////////////////. !doit nbval=3.
In the following macro, the macro variable !myvar takes on the values ln(1), ln(2) and ln(3).
DEFINE !test() !LET !cnt=!NULL !LET !val=!EVAL(!values) !DO !myvar !IN (!val) !LET !cnt=!CONCAT(!cnt,!BLANK(1)) COMPUTE !CONCAT(var,!LENGTH(!cnt))=!myvar. !DOEND EXECUTE. !ENDDEFINE. !test. * Next line is to see all five digits. FORMAT var1 TO var2 (F8.5).
Another fully commented macro
This macro prints bar charts of categorical variables excluding category values where there are less than n cases.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | SET MPRINT=no. *////////////////////. DEFINE !chart2 (minnb=!TOKENS(1) /cats=!CMDEND) /* minnb = minimum number of cases required in order to chart category value*/. /* cats = names of the categorical variables to be charted*/. COMPUTE dummy=1. !DO !cat !IN (!cats) FILTER OFF. * Sorting is necessary for the MATCH FILES command. SORT CASES BY !cat. !LET !fname=!QUOTE(!CONCAT('C:\temp\',!cat,'.SAV')) AGGREGATE /OUTFILE=!fname /PRESORTED /BREAK=!cat /nb = N(dummy). MATCH FILES /FILE=* /TABLE=!fname /BY !cat. FREQUENCY VARIABLES=!cat. LIST. COMPUTE flag=(nb>=!minnb). FILTER BY flag. GRAPH /BAR(SIMPLE)=COUNT BY !cat /MISSING=REPORT. * Delete the variable nb. MATCH FILES FILE=* /DROP=nb. !DOEND * Delete the variable dummy. MATCH FILES FILE=* /DROP=dummy. !ENDDEFINE. */////////////////// SET MPRINT=yes. !chart2 minnb=5 cats= age prog year. EXECUTE. |
The macro definition starts at line 3 and ends at line 32. The macro is called at line 35.
- I use the
SET MPRINT
command all the time when I write or debug a macro. For greater readability of the Output Window, it is preferable to set it to no before the macro definition starts and to set it to yes (see line 34) before calling the macro. - I like to enclose my macros between special characters which I use just for this purpose. See line 2 and 33. This allows me to locate macros faster in long syntax files.
- The beginning of the macro. The name of the macro is
!chart2
. I like to start macro names with ! this helps to quickly identify macro names in ordinary syntax. The macro requires 2 arguments when called:minnb
andcats
. Thus ifminnb=5
andcats=var1 var2 var3
, the macro will generate bar charts for the categorical variables var1, var2 and var3 where any given category of these variables will only be charted if they have more than 5 cases with that category value. Note that a command terminator is not required at the end of a macro command. (!ENDDEFINE
is more a syntax than a macro command). Since there are 3 tokens (or variables) in !cats, the syntax within the loop will be executed 3 times. - This is a comment. Within macros, it is safer to always begin comments with
/*
and end them with*/
- A second line of comment.
- The variable dummy is used in line 16.
- The
!DO
statement works in conjunction with the!DOEND
in line 29 to form a DO LOOP. Line 7 means that the macro variable !cat will successively take on each of the values contained in the macro variable !cats. The Macro variable !cats is defined in the macro call in line 35. In this example, since cats=age prog year, the macro variable !cat will successively equal each of these 3 categorical names. - This turns the filter off. All cases become visible to SPSS procedures after this line is executed. Note that in line 23 some variables are filtered out. Line 8 thus undo what line 23 has done.
- A comment.
- This sorts the data file by increasing value of the variable contained in the macro variable !cat. (Thus the first time the loop goes through, this will sort the file by order of age; the second time the loop goes through, sorting will be done by order of prog, etc)
- This defines the macro variable !fname. When !cat=age, we have
!fname=!QUOTE(!CONCAT('C:\temp\',age,'.SAV'))
!fname=!QUOTE(C:\temp\age.SAV)
!fname="C:\temp\age.SAV" - This AGGR command (line 12 to 16) counts the number of cases that fall within each category value of the categorical variable !cat. For instance there might be 3 persons aged 25, 7 aged 26, etc. The results are saved in the file whose name is given by the macro variable !fname.
- This sub-command of the AGGREGATE command gives the name of the file which will contain the results
- Since the file was sorted in line 10, this line informs SPSS that it is not necessary to sort the file. When the file is presorted, less memory is required to run the AGGR command. The command also executes faster.
- This instruct the AGGR command to use the variable represented by the macro variable !cat as the Break variable.
- In addition to the break variable, the file of results will contain a variable named nb which will contain the number of cases having each of the different values of the break variable. For instance when !cat=age, the result file might contain cases such as
case 1: age=25, nb=5
case 2: age=26, nb=7
etc - Match the current file with
- the file whose name is in the macro variable !fname
- do the matching on the basis of the variable whose name is in the macro variable !cat. The variable nb is added to the original data file. Thus if case 1 of the original data file has age=25, the value of nb will be 5. Similarly if case 2 of the original data file has age=26, the value of nb will be 7. Similarly if case 3 of the original data file has age=25, the value of nb will be 5, and so on.
- This prints the Frequency Table of var !cat. This is just to allow verification that only the categorical values with at least !minnb cases are entered in the chart graph.
- The list of cases is printed for information purposes. This line should be commented out when you do real jobs.
- The flag variable will equal 1 for cases with a value of nb > !minnb. In other words, only cases for which we want to do a bar chart will have a value of 1. Other cases will have a value of 0.
- This line renders "invisible" cases where flag=0. These cases are ignored by SPSS procedures. It is as if they not in the data file. They remain invisible until a
FILTER OFF
command is encountered. - These
- lines
- print the Bar Charts. Of course you could replace this by any other graph.
- A comment.
- It is necessary to delete the variable nb otherwise lines 17-19 will not be able to add the variable nb (since it will already exist) the next time the loop goes through.
- The end of the syntax to be repeated within the loop. Control goes back to line 7 to process the next variable name contained in the macro variable !cats.
- A comment.
- This deletes the variable dummy which is no longer necessary.
- The end of the macro definition.
- See comment on line 2.
- See comment on line 1.
- This is the macro call. The line should now be clear.
...