TIP: Much ado about python and SPSS Macro

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

TIP: Much ado about python and SPSS Macro

David Marso
Administrator
/* Background:  One of the more frustrating things about SPSS Macro
/* is its inability to resolve simple variable lists of the form VarX TO
VarZ.
/* For example one might want to iterate over a list of variables and do the
following:

FREQUENCIES Var.
GRAPH/BAR(SIMPLE)=COUNT BY var.
 
say:

DEFINE !MyMacro (Varlist !CMDEND)
!DO !VAR !IN (!Varlist)
FREQUENCIES !VAR.
GRAPH/BAR(SIMPLE)=COUNT BY !VAR.
!DOEND
!ENDDEFINE.
!MyMacro Varlist=a b c d e.

/* If one attempts to invoke this using a standard TO convention in the
Varlist argument one gets

!MyMacro Varlist=a TO e.

FREQUENCIES a.
GRAPH/BAR(SIMPLE)=COUNT BY a.
FREQUENCIES TO.
GRAPH/BAR(SIMPLE)=COUNT BY TO.

/* OOPS, won't fly so well...

/* The key to solving this is a simple python function in the spssaux
library.

DATA LIST FREE/ a b c d e f x y z.
BEGIN DATA
5 5 5 5 5 5 5 5 5
END DATA.

BEGIN PROGRAM.
import spssaux  
varlist=spssaux.VariableDict().expand('a TO z')
print varlist
END PROGRAM.

/* This returns us a python list object.
/* ['a', u'b', u'c', u'd', u'e', u'f', u'x', u'y', 'z']

/* Now this is not immediately useful to us in the list format.  
/* A simple solution is to use the python join function.


BEGIN PROGRAM.
import spssaux  
varlist=spssaux.VariableDict().expand('a TO z')
print varlist
joined=" ".join(varlist)
print joined
END PROGRAM.

/* ['a', u'b', u'c', u'd', u'e', u'f', u'x', u'y', 'z']
/* a b c d e f x y z

/*  Of course this can be done in a single line of code

BEGIN PROGRAM.
import spssaux  
print " ".join(spssaux.VariableDict().expand('a TO z'))
END PROGRAM.
/* a b c d e f x y z


/* To make this useful we can embed this is a python function and pass
arguments .
BEGIN PROGRAM.
import spssaux  
def myfunction (myvarlist):
  print " ".join(spssaux.VariableDict().expand(myvarlist ))
END PROGRAM.

BEGIN PROGRAM.
myfunction (myvarlist='a TO z')
END PROGRAM.

/* So we have the basic tools required to resolve TO for use in macros.

DEFINE !myMacro ( !POS !CMDEND)
ECHO 'INSIDE !myMacro'.
ECHO !QUOTE ( !1).
!DO !v !IN  ( !1)
ECHO !QUOTE ( !v).
!DOEND
!ENDDEFINE.

BEGIN PROGRAM.
import spssaux  
import spss
def myfunctionX (myvarlist):
  spss.Submit( '!myMacro  ' + "
".join(spssaux.VariableDict().expand(myvarlist )))
END PROGRAM.

BEGIN PROGRAM.
myfunctionX (myvarlist='a TO z')
END PROGRAM.

/* Let's take it a step further and pass our macro name as an additional
parameter .

BEGIN PROGRAM.
import spssaux  
import spss
def myfunctionY (mymacro, myvarlist):
  spss.Submit( mymacro + " " + "
".join(spssaux.VariableDict().expand(myvarlist )))
END PROGRAM.

BEGIN PROGRAM.
myfunctionY (mymacro='!mymacro',myvarlist='a TO z')
END PROGRAM.


/* FINALLY, most macros take more than a simple variable list and frequently
/* have named rather than positional arguments.

DEFINE  !TestVarlist (VariableList !CHAREND ("/") /A !CHAREND ("/")/B
!CHAREND ("/") /C !CMDEND )
!DO !Var !IN (!VariableList)
ECHO  !QUOTE (!Var).
!DOEND
ECHO  !QUOTE (!CONCAT ("A=", !A, " B=", !B, " C=",!C)).
!ENDDEFINE.

BEGIN PROGRAM.
def ResolveVarlist (MACROName,VarParam,VarList, MacroArgs):
  spss.Submit( '%s %s = %s / %s.'
  %(MACROName, VarParam," ".join( spssaux.VariableDict().expand(VarList)
),MacroArgs))      
END PROGRAM.

/* The above simply inserts the elements in the comma separated list
sequentially into the
/* formula '%s %s = %s / %s.'.  It will resolve to:
/* MACROName VarParam=VarList-expanded- / MacroArgs.

DATA LIST FREE/ var1 TO var10.
BEGIN DATA
1 2 3 4 5 6 7 8 9 10
END DATA.

SET MPRINT ON PRINTBACK ON.

BEGIN PROGRAM.
import spssaux  
import spss
ResolveVarlist (MACROName='!TestVarlist',
                VarParam='VariableList',
                VarList='var1 TO var3 var5 var7 TO var9',
                MacroArgs='A=what / B=is / C=this')
END PROGRAM.

/*You can also call the macro from native SPSS code using: */

!TestVarlist
  VariableList=var1 var2 var3 var5 var7 var8 var9
 / A=what
 / B=is
 / C=this .

/* The following business simply generalizes these ideas to support multiple
arbitrary lists  */.
DEFINE !MyMacro (VarList1 !CHAREND("/")
                /VarList2 !CHAREND("/")
                /A  !CHAREND("/")
                /B  !CHAREND("/")
                /C  !CHAREND("/")
                /D !CMDEND)
ECHO "Inside of Macro !MyMacro".
ECHO !QUOTE(!CONCAT("VarList1=",!VarList1)).
!DO !V !IN (!VarList1)
ECHO !QUOTE(!V).
!DOEND
ECHO !QUOTE(!CONCAT("VarList2=",!VarList2)).
!DO !V !IN (!VarList2)
ECHO !QUOTE(!V).
!DOEND
ECHO  !QUOTE (!CONCAT ("A=", !A, " B=", !B, " C=",!C, " D=",!D)).    
!ENDDEFINE.


BEGIN PROGRAM.
def py_InvokeMacroWithMultipleVariableLists
(MacroName,VariableLists,OtherParameters ):
  SPSScode=MacroName
  for list in VariableLists.split(","):
    parts=list.split("=")
    SPSScode += " ".join([' ',parts[0],'=',"
".join(spssaux.VariableDict().expand(parts[1])),'/'])
  spss.Submit( SPSScode + OtherParameters)
  print 'Back home in py_InvokeMacroWithMultipleVariableLists'
END PROGRAM.

MATRIX.
SAVE {1:10}/OUTFILE * /VARIABLES var1 TO var10.
END MATRIX.

BEGIN PROGRAM.
import spssaux  
import spss
print 'Prior to function call'
py_InvokeMacroWithMultipleVariableLists (
  MacroName='!MyMacro',
  VariableLists='VarList1=var1 TO var3 var5,\
                 VarList2=var5 var7 TO var9',            
  OtherParameters='A=what /B=a /C=cool /D=trick')
print 'All the way back'
END PROGRAM.

OUTPUT:
Inside of Macro !MyMacro
VarList1=var1 var2 var3 var5
var1
var2
var3
var5
VarList2=var5 var7 var8 var9
var5
var7
var8
var9
A=what B=a C=cool D=trick

/*Could be invoked natively through: */.
!MyMacro VarList1=var1 var2 var3 var5
        /VarList2=var5 var7 var8 var9
        /A=what
        /B=a
        /C=cool
        /D=trick .






-----
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
--
Sent from: http://spssx-discussion.1045642.n5.nabble.com/

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: TIP: Much ado about python and SPSS Macro

Jon Peck
Nice post, David.

Here are a few additional tips.

You can use the spssaux.VariableDict class to make a macro smarter.  For example, you might want to exclude string variables from a TO list if it is going to invoke DESCRIPTIVES.  The VariableDict constructor allows you to filter a variable list according to various variable properties such as measurement level and variable type.  So you could write
spssaux.VariableDict(<various filter criteria>).expand(...)
whether or not it has a TO list or ALL.
Full details on this class can be found by opening the spssaux.py module in any plain text editor and reviewing the comments at the top of the class.

For users who don't want to write any Python code at all (but still want to use macro?), you can use the SPSSINC SELECT VARIABLES extension command to create a macro that lists only variables that match various metadata criteria such as patterns in names, variable type, variable level, role, and custom attributes.  The macro could be used directly or referenced inside other macro code.  From the syntax window you can see the full help for this or other installed extension commands by placing the cursor on an instance of the command and pressing F1 (v23 or later) or by running something like
SPSSINC SELECT VARIABLES /HELP.
This extension command also has a dialog box interface available from the Utilities menu.



On Tue, Nov 7, 2017 at 7:27 AM, David Marso <[hidden email]> wrote:
/* Background:  One of the more frustrating things about SPSS Macro
/* is its inability to resolve simple variable lists of the form VarX TO
VarZ.
/* For example one might want to iterate over a list of variables and do the
following:

FREQUENCIES Var.
GRAPH/BAR(SIMPLE)=COUNT BY var.

say:

DEFINE !MyMacro (Varlist !CMDEND)
!DO !VAR !IN (!Varlist)
FREQUENCIES !VAR.
GRAPH/BAR(SIMPLE)=COUNT BY !VAR.
!DOEND
!ENDDEFINE.
!MyMacro Varlist=a b c d e.

/* If one attempts to invoke this using a standard TO convention in the
Varlist argument one gets

!MyMacro Varlist=a TO e.

FREQUENCIES a.
GRAPH/BAR(SIMPLE)=COUNT BY a.
FREQUENCIES TO.
GRAPH/BAR(SIMPLE)=COUNT BY TO.

/* OOPS, won't fly so well...

/* The key to solving this is a simple python function in the spssaux
library.

DATA LIST FREE/ a b c d e f x y z.
BEGIN DATA
5 5 5 5 5 5 5 5 5
END DATA.

BEGIN PROGRAM.
import spssaux
varlist=spssaux.VariableDict().expand('a TO z')
print varlist
END PROGRAM.

/* This returns us a python list object.
/* ['a', u'b', u'c', u'd', u'e', u'f', u'x', u'y', 'z']

/* Now this is not immediately useful to us in the list format.
/* A simple solution is to use the python join function.


BEGIN PROGRAM.
import spssaux
varlist=spssaux.VariableDict().expand('a TO z')
print varlist
joined=" ".join(varlist)
print joined
END PROGRAM.

/* ['a', u'b', u'c', u'd', u'e', u'f', u'x', u'y', 'z']
/* a b c d e f x y z

/*  Of course this can be done in a single line of code

BEGIN PROGRAM.
import spssaux
print " ".join(spssaux.VariableDict().expand('a TO z'))
END PROGRAM.
/* a b c d e f x y z


/* To make this useful we can embed this is a python function and pass
arguments .
BEGIN PROGRAM.
import spssaux
def myfunction (myvarlist):
  print " ".join(spssaux.VariableDict().expand(myvarlist ))
END PROGRAM.

BEGIN PROGRAM.
myfunction (myvarlist='a TO z')
END PROGRAM.

/* So we have the basic tools required to resolve TO for use in macros.

DEFINE !myMacro ( !POS !CMDEND)
ECHO 'INSIDE !myMacro'.
ECHO !QUOTE ( !1).
!DO !v !IN  ( !1)
ECHO !QUOTE ( !v).
!DOEND
!ENDDEFINE.

BEGIN PROGRAM.
import spssaux
import spss
def myfunctionX (myvarlist):
  spss.Submit( '!myMacro  ' + "
".join(spssaux.VariableDict().expand(myvarlist )))
END PROGRAM.

BEGIN PROGRAM.
myfunctionX (myvarlist='a TO z')
END PROGRAM.

/* Let's take it a step further and pass our macro name as an additional
parameter .

BEGIN PROGRAM.
import spssaux
import spss
def myfunctionY (mymacro, myvarlist):
  spss.Submit( mymacro + " " + "
".join(spssaux.VariableDict().expand(myvarlist )))
END PROGRAM.

BEGIN PROGRAM.
myfunctionY (mymacro='!mymacro',myvarlist='a TO z')
END PROGRAM.


/* FINALLY, most macros take more than a simple variable list and frequently
/* have named rather than positional arguments.

DEFINE  !TestVarlist (VariableList !CHAREND ("/") /A !CHAREND ("/")/B
!CHAREND ("/") /C !CMDEND )
!DO !Var !IN (!VariableList)
ECHO  !QUOTE (!Var).
!DOEND
ECHO  !QUOTE (!CONCAT ("A=", !A, " B=", !B, " C=",!C)).
!ENDDEFINE.

BEGIN PROGRAM.
def ResolveVarlist (MACROName,VarParam,VarList, MacroArgs):
  spss.Submit( '%s %s = %s / %s.'
  %(MACROName, VarParam," ".join( spssaux.VariableDict().expand(VarList)
),MacroArgs))
END PROGRAM.

/* The above simply inserts the elements in the comma separated list
sequentially into the
/* formula '%s %s = %s / %s.'.  It will resolve to:
/* MACROName VarParam=VarList-expanded- / MacroArgs.

DATA LIST FREE/ var1 TO var10.
BEGIN DATA
1 2 3 4 5 6 7 8 9 10
END DATA.

SET MPRINT ON PRINTBACK ON.

BEGIN PROGRAM.
import spssaux
import spss
ResolveVarlist (MACROName='!TestVarlist',
                VarParam='VariableList',
                VarList='var1 TO var3 var5 var7 TO var9',
                MacroArgs='A=what / B=is / C=this')
END PROGRAM.

/*You can also call the macro from native SPSS code using: */

!TestVarlist
  VariableList=var1 var2 var3 var5 var7 var8 var9
 / A=what
 / B=is
 / C=this .

/* The following business simply generalizes these ideas to support multiple
arbitrary lists  */.
DEFINE !MyMacro (VarList1 !CHAREND("/")
                /VarList2 !CHAREND("/")
                /A  !CHAREND("/")
                /B  !CHAREND("/")
                /C  !CHAREND("/")
                /D !CMDEND)
ECHO "Inside of Macro !MyMacro".
ECHO !QUOTE(!CONCAT("VarList1=",!VarList1)).
!DO !V !IN (!VarList1)
ECHO !QUOTE(!V).
!DOEND
ECHO !QUOTE(!CONCAT("VarList2=",!VarList2)).
!DO !V !IN (!VarList2)
ECHO !QUOTE(!V).
!DOEND
ECHO  !QUOTE (!CONCAT ("A=", !A, " B=", !B, " C=",!C, " D=",!D)).
!ENDDEFINE.


BEGIN PROGRAM.
def py_InvokeMacroWithMultipleVariableLists
(MacroName,VariableLists,OtherParameters ):
  SPSScode=MacroName
  for list in VariableLists.split(","):
    parts=list.split("=")
    SPSScode += " ".join([' ',parts[0],'=',"
".join(spssaux.VariableDict().expand(parts[1])),'/'])
  spss.Submit( SPSScode + OtherParameters)
  print 'Back home in py_InvokeMacroWithMultipleVariableLists'
END PROGRAM.

MATRIX.
SAVE {1:10}/OUTFILE * /VARIABLES var1 TO var10.
END MATRIX.

BEGIN PROGRAM.
import spssaux
import spss
print 'Prior to function call'
py_InvokeMacroWithMultipleVariableLists (
  MacroName='!MyMacro',
  VariableLists='VarList1=var1 TO var3 var5,\
                 VarList2=var5 var7 TO var9',
  OtherParameters='A=what /B=a /C=cool /D=trick')
print 'All the way back'
END PROGRAM.

OUTPUT:
Inside of Macro !MyMacro
VarList1=var1 var2 var3 var5
var1
var2
var3
var5
VarList2=var5 var7 var8 var9
var5
var7
var8
var9
A=what B=a C=cool D=trick

/*Could be invoked natively through: */.
!MyMacro VarList1=var1 var2 var3 var5
        /VarList2=var5 var7 var8 var9
        /A=what
        /B=a
        /C=cool
        /D=trick .






-----
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
--
Sent from: http://spssx-discussion.1045642.n5.nabble.com/

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD



--
Jon K Peck
[hidden email]

===================== To manage your subscription to SPSSX-L, send a message to [hidden email] (not to SPSSX-L), with no body text except the command. To leave the list, send the command SIGNOFF SPSSX-L For a list of commands to manage subscriptions, send the command INFO REFCARD