Extracting variable labels and using them as a string in SPSS macro - macros

I am working on an SPSS macro that aims to create custom tables, each with a title that includes the variable label (as opposed to name, which it currently does). As a reduced example:
DEFINE !Ctables_loop (byvarlist = !CMDEND)
!DO !byvar !IN (!byvarlist)
CTABLES /titles Title= !QUOTE(!CONCAT('Table: ', !byvar)).
!DOEND
!ENDDEFINE.
Ideally, this solution would only involve the SPSS Macro language.
Thank you!

possibly this should do it:
* run before running the macro.
SET TVars=Labels OVars=Labels .
You can reach this also through EDIT => OPTIONS => OUTPUT.

As a partial solution:
By using '>TABLE' in the title section of the Ctables specification you can specify the variables used in the table using their labels. It is a bit inflexible as you are stuck with more than just the label (e.g. "var1 by var2" rather than having direct access to var1 label and var2 label), but I used a find-and-replace function to round off the formatting for the entire batch of tables after exporting.
I hope this helps other people with a similar problem.
The source and more information: https://www.ibm.com/support/knowledgecenter/ko/SSLVMB_21.0.0/com.ibm.spss.statistics.help/syn_ctables_titles_titles_captions_and_corner_text.htm

Related

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

Stata: Edit each element of a global (which contains a list of variables)

This is probably just a short syntax question. I have:
clear all
macro drop _all
global variables var1 var2
and I want
global means m_var1 m_var2
which I have generated elsewhere. The goal is to use both globals in a Mundlak regression (like reg depvar $variables $means and not having to calculate/include the means by hand for different specifications. My idea was something along the lines of:
global means "m_`variables'"
but that simply ignores the variables global. Again, sorry for the R-think...
Edit: My strategy: I am trying to write a program which runs models (Mundlak/Chamberlain random effects logit, see Wooldridges Panel book 2nd ed p. 487) on several distinct lists of variables and returns graphs of regression results. This should be done such that I only have to change the globals/locals specifying these variables in the beginning. Thus, I need to have code that creates time averages of the globals and uses these and the original global in the logit specification.
I'm not convinced your general strategy is a good one, but I don't have information on the issue you face, so I won't comment much more.
I'll state that using locals is a better idea if you can spare the globals, and that you can redefine the contents of a macro using a loop:
clear all
set more off
local variables var1 var2
// original
local means "m_`variables'"
// loop
local means2
foreach v of local variables {
local means2 `means2' m_`v'
}
display "`means'"
display "`means2'"

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.

update global variables from a parameter table without using `run` on a string

As documented, using run on a string runs into problems when local variables (including procedure parameters) are involved. So, what is the recommended way to achieve the following goal?
I have parameter tables (p1, p2, etc) specifying values for global variables, by name (i.e., table keys are strings, corresponding to the names of the global variables). Given a name (as a string) and a parameter table, I want to set the named global variable to the table value. E.g., if we were to use run, and if the values were all numbers, we might do something like this:
to update [#nm #tbl]
let %tval table:get #tbl #nm
run (word "set " #nm " " %tval)
end
What is the recommended way to do this, avoiding strings (due to the warning in the docs)?
As an additional complication, some table values may be tasks.
Extension of the question:
Following up on my coment of Oct 9, I found that if I isolate the assignment to a procedure, I can also successively make global assignments with tasks. E.g.,
to setGlobalTasks [#name #table]
;; #name : string, name of global variable
;; #table : table, maps names (strings) to values (reporter tasks)
let %tval table:get #table #name
run (word "set " #name " %tval")
end
Seth has provided some assurance that proceeding this way will continue to work in NetLogo when the assigned values are numbers. Is this going to be risky when the assigned values are tasks? Does it pose any risks in NetLogo 5.1?
Note: probably this extension of the question should be in the comments, but I could not format code blocks in a comment.
Your original approach, where %tval is outside of double quotes, only works with table values that are ordinary values like numbers, lists, or strings, values that can survive a round trip to string and back. (If you had trouble in practice, my guess would be that run got confused when you tried to combine it with foreach, as in the code you posted at http://groups.google.com/forum/#!topic/netlogo-devel/m5rnPEsxR44 . I believe this can be worked around by writing a standalone procedure like the one in your question.)
Your revised code, where %tval is inside the double quotes, and the whole thing is isolated in a separate procedure, is correct and should work for all possible table values. It should work fine in both NetLogo 5.0 and 5.1, and almost certainly in 6.0 too if there ever is a 6.0.
(In Tortoise, it wouldn't work, since run probably won't support strings at all in Tortoise.)
Tangent on reflection:
run on strings is kinda ugly. In situations where you want "reflective" setting of variables by name, where the name is stored in a string computed at runtime, it would be nice if there were an extension that supported this directly. The good news is, the code for the extension would be quite short and simple. The required methods within NetLogo, that the extension would call, already exist. The bad news is, writing a NetLogo extension of any kind is only easy if you're comfortable writing and compiling simple Java (or Scala) code. Anyway, it would be great if such a "reflection" extension existed. But in the meantime, you're safe with what you have.

How do I search for variable names in the editor?

In functions, the MATLAB editor displays a warning when a defined variable is not subsequently used before the function ends or before the variable is overwritten. This obviously tells me that the editor has a way of searching for occurrences of given variables in the code.
Can I do this manually? The Find function is obviously limited here, since it only searches the body of text in the editor window (including comments) for matches of the search string. This makes it very inconvenient when searching for variables with short names (such as the by me commonly used "a").
I cannot tell you about previous versions of the built-in editor, but at least from 2011b, the righthand side margin of the editor creates color tags for:
Warnings, tagged in orange
Errors, tagged in red color
Variable/function occurrence: tagged in dark gray, for selected text.
The third of them is what you are looking for. You just have to double click on a variable name or a function name to select it and your Matlab editor will automatically highlight the rest of the occurrences of the very same identifier, tagging them on the righthand side ribbon with the grey mark I mentioned above.
You can limit the search to match case and whole word, which will give you only this variable, either in comment or not.
BTW, you shouldn't use variable names like a,b,c. It makes the code harder to read and to maintain. Even if you have dummy variables like in loops and temporary ones, use for example indexFiles, or tempValue
You can also use some regular expression to match the variable names in your code.
If you'll assume that any variable name is separated from the rest of the code by any of linefeed tab space ! " # $ % & ' ( ) * + , - . / : ; < = > ? [ \ ] ^ `` { | } ~, then you can create a small function that takes the function name as input and outputs the lines in which the variable name is mentioned. However, this approach doesn't separate function names and variable names, but you should have some standard for separating them anyway. I use this approach to change variable names of my MATLAB code (but my code for that is written in awk, not in MATLAB).
And I wonder what you'll do when you have a complex program with thousands or tens of thousands of lines of code and your variables are named a, b, c and so on...