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.
Related
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.
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
On the Ti-84 and Ti-89, I know that one can use "Define" to define a function with multiple variables. However, is there a way to use "Prompt" or something of the sort to define a function?
Suppose we have a very simple program that evaluates an expression (only an example):
Prompt a,b,c
Disp a^5-sqrt(c)/b
Instead of having to manually go into the Program Editor every single time to change the expression, how can we automatically Prompt the user to Define a function such as eq(a,b,c)=a^5-sqrt(c)/b?
Yes you can! Use y-variables.
Functions are stored as y-variables on the TI-84, and these variables can be accessed by going to the VARS>Y-VARS>Function... menu. Then you can prompt the user for the equation with the y-variable being the input, like this:
Prompt Y1
Keep in mind that this is a string, meaning you need quotation marks at the beginning and end of your function when putting it in as the user. This can be cumbersome, and can be avoided by using Input instead, and storing it into a String variable (found in VARS>String...) first then to a y-variable.
Input "f(a,b,c): ",Str1
Str1→Y1
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.
I have a rather bulky program that I've been running as a script from the MATLAB command line. I decided to clean it up a bit with some nested functions (I need to keep everything in one file), but in order for that to work it required me to also make the program itself a function. As a result, the program no longer runs in the base workspace like it did when it was a script. This means I no longer have access to the dozens of useful variables that used to remain after the program runs, which are important for extra calculations and information about the run.
The suggested workarounds I can find are to use assignin, evalin, define the variables as global, or set the output in the definition of the now function-ized program. None of these solutions appeal to me, however, and I would really like to find a way to force the workspace itself to base. Does any such workaround exist? Or is there any other way to do this that doesn't require me to manually define or label each specific variable I want to get out of the function?
Functions should define clearly input and output variables. Organizing the code differently will be much more difficult to understand and to modify later on. In the end, it will most likely cost you more time to work with an unorthodox style than investing in some restructuring.
If you have a huge number of output variables, I would suggest organizing them in structure arrays, which might be easy to handle as output variables.
The only untidy workaround I can imagine would use whos, assignin and eval:
function your_function()
x = 'hello' ;
y = 'world' ;
variables = whos ;
for k=1:length(variables)
assignin('base',variables(k).name,eval(variables(k).name))
end
end
But I doubt that this will help with the aim to clean up your program. As mentioned above I suggest ordering things manually in structures:
function out = your_function()
x = 'hello' ;
y = 'world' ;
out.x = x ;
out.y = y ;
end
If the function you would like to define are simple and have a single output, one option is to use anonymous functions.
Another option is to store all the variable you would like to use afterwards in a struct and have your big function return this struct as an output.
function AllVariables = GlobalFunction(varargin);
% bunch of stuff
AllVariables= struct('Variable1', Variable1, 'Variable2', Variable2, …);
end