SPSS Macro to create new variable based on 2 existing variables - macros

I am trying to write a macro to perform the following operation:
do if SYSMIS(V2).
compute V3=V1.
ELSE.
compute V3=V2.
end if.
I tried several approaches, but it always gets stuck on the compute command.

One important thing, you should know about SPSS macros is, that the SPSS macro language is just a "string parser". This means, the text within a DEFINE !ENDDEFINE block is parsed and as an output a syntax code will be created. The variables (beginning with "!") will be substituted by the strings(values) assigned to these variables.
So the line COMPUTE !var1 = !var2. will produce a compute command, with the variables names assigned to !var1 and !var2.
On the other hand a command like !IF (SYSMIS(!Var2)) !Var1 = !Var3.
makes no sense, because what you actually want is to execute an IF command, while !IF is a conditional macro directive, which 'decides' if some piece of syntax code be generated by the macro (if the condition is met), or not.
So you solution would look something like this:
DEFINE !crevar ( !POSITIONAL !TOKENS(1)
/!POSITIONAL !TOKENS(1)
/!POSITIONAL !TOKENS(1))
DO IF SYSMIS(!2).
compute !3=!1.
ELSE.
compute !3=!2.
END IF.
!ENDDEFINE.
* Macro call.
!crevar V1 V2 V3.
EXECUTE.
The macro call above, will be evaluated by the macro parser to the code you have given in your question.

Related

Stata and global variables

I am working with Stata.
I have a variable called graduate_secondary.
I generate a global variable called outcome, because eventually I will use another outcome.
Now I want to replace the variable graduate if a condition relative to global is met, but I get an error:
My code is:
global outcome "graduate_secondary"
gen graduate=.
replace graduate=1 if graduate_primary==1 & `outcome'==1
But i receive the symbol ==1 invalid name.
Does anyone know why?
Something along those lines might work (using a reproducible example):
sysuse auto, clear
global outcome "rep78"
gen graduate=.
replace graduate=1 if mpg==22 & $outcome==3
(2 real changes made)
In your example, just use
replace graduate=1 if graduate_primary==1 & $outcome==1
would work.
Another solution is to replace global outcome "graduate_secondary" with local outcome "graduate_secondary".
Stata has two types of macros: global, which are accessed with a $, and local, which are accessed with single quotes `' around the name -- as you did in your original code.
You get an error message because a local by the name of outcome has no value assigned to it in your workspace. By design, this will not itself produce an error but instead will the reference to the macro will evaluate as a blank value. You can see the result of evaluating macro references when you type them by using display as follows. You can also see all of the macros in your workspace with macro dir (the locals start with an underscore):
display `outcome'
display $outcome
Here is a blog post about using macros in Stata. In general, I only use global macros when I have to pass something between multiple routines, but this seems like a good use case for locals.

Do IF and Compute in spss macros

I have the following spss syntax:
DO IF SYSMIS(V5).
COMPUTE V5 = LAG(V5).
END IF.
EXE.
It works fine. However I'd like to repeat the same process for several variables. I tried to write a macro to achieve this but I keep getting error messages. This was my attempt:
define filldown (!positional !cmdend).
do if sysmis(!1).
compute !1 = lag (!1).
end if.
execute.
!enddefine.
!filldown V5 age wt htm.
How do I write a macro that will work (I'm new to macros)?
#horace_vr's do repeat solution is definitely the right approach for this case.
The following is just to learn something about macros while you're at it.
First of all, you can use your present macro for each variable separately, but you need to use the original macro call (don't add "!"), so:
filldown V5.
filldown age.
....
But of course you can create a loop within the macro, like this:
define filldown (!positional !cmdend).
!do !vr !in (!1)
do if sysmis(!vr).
compute !vr = lag (!vr).
end if.
execute.
!doend
!enddefine.
Now you can use the macro call once with the complete list:
filldown V5 age wt htm.
The macro is simply a text substitution function. It will literally replace your !1 with whatever argument you are providing when calling the macro (V5 age wt htm).
To keep things simple, I would recommend using a simple do repeat command, instead of a macro, which may be little uncomfortable to use if you are not familiar with them
do repeat varlist=V5 age wt htm.
if sysmis(varlist) varlist=lag(varlist).
end repeat.
exe.
P.S.: If you really want to use your macro, you need to call it for each variable separately.
Set Auto sum To lisp. Other variables cannot be quantified. A quantifier is a lisp command which shows the next sum of the program.
Fill ! V2 you need to call V before rv,
V2 is positional, that is command is read only after changes has been made.
Define filldown ! v5
do! rv! fill! V2
..........
End

Macro call vs macro definition environment in Julia

I am trying to make sense out of a statement in the Julia's Metaprogramming documentation on macro hygiene. The documentation claims that
Julia’s macro expander solves these problems in the following way. First, variables within a macro result are classified as either local or global. A variable is considered local if it is assigned to (and not declared global), declared local, or used as a function argument name. Otherwise, it is considered global. Local variables are then renamed to be unique (using the gensym() function, which generates new symbols), and global variables are resolved within the macro definition environment. Therefore both of the above concerns are handled; the macro’s locals will not conflict with any user variables, and time and println will refer to the standard library definitions.
I wrote a small program to see whether global variables were indeed resolved in the macro definition environment. I wrote the following:
f(x) = x + 100
macro g() # According to Julia docs, ...
:(f(x) + 5) # ... f is global, x is local, right?
end # if so, f should refer to the f above?
(function main()
local x = 3
f(x) = x - 100 # f in the call environment subtracts 100
println(#g()) # So why does this do -92?
end)()
If I am to understand the Julia docs correctly, part of macro hygiene is to make sure whichever functions are called in the macro's returned expression don't get hijacked by functions of the same name inside the caller's environment. But this is exactly what happens here, the function f that was used was the one that was defined locally.
I would have thought I would have to use esc in order to use the f in scope at the point of call. But this is not so, why?
And in addition, I noticed that the the variable x inside the macro result is considered local, so a new gensymed variable name should have been generated for it, so as not to clash with the x in the macro call environment. But this did not happen either!
How am I to read the documentation to make any sense out the reason that esc need not be used here?
EDIT (CLARIFICATION)
When I claim f is global and x is local, according to the docs, I do so because I see that x is used as a function argument. I understand x is not being written to nor declared local, and it certainly looks global, but those docs claim function arguments should be global too!
I know the usual part of hygiene where the gensymming of locals ensures that variables of the same name in the macro caller's context are not inadvertently plastered. However, the docs claim that for functions, the ones that are seen in the macro definition's context are protected from the caller using their own. This is the part that makes no sense to me, because my experiment shows otherwise.
This is a bug. It's an old issue of Julia 0.5 and earlier, and has been fixed in Julia 0.6. See https://github.com/JuliaLang/julia/issues/4873 for more information.

combine compute, loop and concat

I would like to run the following syntax on lots of variables. Thus, I'd like to loop over a bunch of variables.
The syntax is the following:
compute v3a_mit = v3a.
recode v3a_mit
(-9998=2) (sysmis=9).
exe.
In this case, however, the syntax only concerns the variable "v3a".I have some other variables (v3b, v3c, v3d...) for which I would like to execute this syntax.
So, the loop should look like this.
DO REPEAT X=v3a to v3z
compute concat(X,'_mit') = X.
recode concat(X,'_mit')
(-9998=2) (sysmis=9).
exe.
END REPEAT.
So, within the loop, new variables shall be created which get a new name depending on the variable which is executed in the loop. The "SHIFT VALUES VARIABLE" command would be ideal (with shift=0) but this command cannot be used within a loop. Unfortunately "compute concat(X,'_mit')" does not work either.
CONCAT is a function for manipulating the values of string variables. So you can't use it for defining a variable name.
However you can make use of the !CONCAT function inside of a SPSS macro.
You can use the following macro to recode a set of variables.
DEFINE !recodeVars (vars = !CMDEND)
* for every variable in the 'vars' list do the RECODE INTO command.
!DO !var !IN (!vars)
RECODE !var (-9998=2) (sysmis=9) INTO !CONCAT(!var, '_mit').
!DOEND
!ENDDEFINE.
* macro call.
!recodeVars vars = v3a v3b v3c v3d.
Here, I used the RECODE INTO command, instead of one COMPUTE and a following RECODE command. But of course the principle of how to use the !CONCAT command would be the same for the COMPUTE operation.
However you can't call the macro in way like this !recodeVars vars = v3a TO v3z. In that case the macro would try perform the RECODE for the variables "v3a", "TO" and "v3z". You have to call this macro with the whole list of variables you want to recode.
This might be a lot of typing. As an easy way to avoid the typing, you could produce a SPSS command via the SPSS Menu (for example Analize -> Descreptive Statistics -> Frequencies) Then select the variables you want to recode (select the first variable, hold the SHIFT key and select the last variable) and then press the paste button. The Frequency command with the list of variables will be pasted to your syntax. You can now copy paste the variable list to your macro call.
If you have the Python integration plugin installed you could also use this small python block to retrieve the varlist between two variables.
BEGIN PROGRAM.
import spss,spssaux
variables = 'v3a to v3z' #Specify variables
varlist = spssaux.VariableDict().expand(variables)
spss.SetMacroValue('!varlist', ' '.join(varlist))
END PROGRAM.
This creates a macro named "!varlist" which expands to the list of variables when called.
You can now call the "!recodeVars" macro the following way: !recodeVars vars = !varlist.
If you don't have the python plugin installed (and don't want to use manual typing or copy and pasting) you can get the full variable list with the use of the "!DefList" macro from Raynald's SPSS Tools.
By the way, you can also make use of a macro for the SHIFT VALUES command.

what is the difference between 'define as' to 'define as computed' in specman?

The difference between the two is not so clear from the Cadence documentation.
Could someone please elaborate on the difference between the two?
A define as macro is just a plain old macro that you probably know from other programming languages. It just means that at some select locations in the macro code you can substitute your own code.
A define as computed macro allows you to construct your output code programmatically, by using control flow statements (if, for, etc.). It acts kind of like a function that returns a string, with the return value being the code that will be inserted in its place by the pre-processor.
With both define as and define as computed macros you define a new syntactic construct of a given syntactic category (for example, <statement> or <action>), and you implement the replacement code that replaces a construct matching the macro match expression (or pattern).
In both cases the macro match expression can have syntactic arguments that are used inside the replacement code and are substituted with the actual code strings used in the matched code.
The difference is that with a define as macro the replacement code is just written in the macro body.
With a define as computed macro you write a procedural code that computes the desired replacement code text and returns it as a string. It's effectively a method that returns string, you can even use the result keyword to assign the resulting string, just like in any e method.
A define as computed macro is useful when the replacement code is not fixed, and can be different depending on the exact macro argument values or even semantic context (for example, in some cases a reflection query can be used to decide on the exact replacement code).
(But it's important to remember that even define as computed macros are executed during compilation and not at run time, so they cannot query actual run time values of fields or variables to decide on the resulting replacement code).
Here are some important differences between the two macro kinds.
A define as macro is more readable and usually easier to write. You just write down the code that you want to be created.
Define as computed macros are stronger. Everything that can be implemented with define as, can also be implemented with define as computed, but not vice versa. When the replacement code is not fixed, define as is not sufficient.
A define as macro can be used immediately after its definition. If the construct it introduces is used in the statement just following the macro, it will already be matched. A define as computed macro can only be used in the next file, and is not usable in the same file in which the macro is defined.