I wish to use select into: to create a global variable in a macro module.
%macro example();
proc sql noprint;
select x into:DDD from aaa;
quit;
%mend;
The problem is I wish to create a global variable DDD, in this example we can only get the local variable DDD.
Thanks.
Wayne.
I don't think you can control the scope within Proc SQL. You an use call symputx in a data step to specify the scope.
However, macro variables created in open code, via Proc SQL, are global by default. Within a macro it will be local.
As noted in the comments you can also explicitly declare a macro variable as global using
%global macro_variable_name;
As #Reeza pointed out you can simply specify that you want the macro-variable global at the beginning of your macro with the %global DDD; statement.
Just one focus point:
sometimes you can recall a macro from inside another macro.
In this case you could have the same macro-variable pre-existing as local in the outer-macro.
Recalling the inner-macro with inside a %global statement you could encounter an error due to the conflict between the global and local definition of the same macro-variable (ERROR: Attempt to %GLOBAL a name (XXX) which exists in a local environment.).
In this case you can:
pre-define the macro-variable as global also in the outer-macro
pre-define the macro-variable as local (or simply give it a (non-)value with a %let, sql into: or call symput) in the outer-macro avoiding the %global statement in the inner-macro
depending on the macro-variable visibility requirement you have.
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 below macro, PARAMETERS, that assigns values to five variables and creates the table MD_WRK.SUB_ID. My issue is that the code within the macro worked fine until I placed it within the macro. Now the variables are not being populated and the PROC SQL for table creation is not being executed. When I remove "%MACRO" the PROC SQL colors change to indicate that it is correct, but when within the macro they are greyed out. I can't understand why the code within the macro is being invalidated.
%MACRO PARAMETERS;
%GLOBAL FDMVAR1 FDMVAR2 UPBOUND LOBOUND GROUPVAR;
%LET FDMVAR1 ='31Mar2017:0:0:0'dt; /* Start Date */
%LET FDMVAR2 = '30Apr2017:0:0:0'dt; /* End Date */
%LET UPBOUND = .01;
%LET LOBOUND = -.01;
%LET GROUPVAR = 1;
PROC SQL;
CREATE TABLE MD_WRK.SUB_ID
(SUBSYS_ID char(6));
INSERT INTO MD_WRK.SUB_ID
VALUES('CBS')
VALUES('CDS')
VALUES('DDA')
VALUES('IMCOR')
VALUES('ODL')
VALUES('OEC')
VALUES('OEC2')
VALUES('SAV')
VALUES('PWRCDS')
VALUES('SMARTY')
VALUES('MRKTLN')
;
QUIT;
%MEND PARAMETERS;
When you say "PROC SQL colors change" if you are referring to the color of the code as shown in Enterprise Guide or some other client that does automatic syntax coloring for you, I would just ignore the coloring. There are some small glitches in the syntax coloring, but note that these colors are not part of the code, so even when the code coloring suggests something may be wrong, it will execute fine as long as the syntax is correct.
If you turn on options mprint; and invoke your macro, the log should show that the dataset MD_WRK.SUB_ID was created (or give you an error message explaining why it wasn't created).
As #Reeza mentioned, the macro variables your create are probably being created as local macro variables, so they will not exist outside of the macro. If you were to declare them as %global inside the macro, they would exist outside of the macro, because they would be stored in the global symbol table. But usually there are better approaches then relying on global macro variables.
I am stuck writing a piece of code that features macro variables of the form &&var&i. Let me give a small example:
%macro test;
%let i=1;
%let name1=Tom;
%put &&name&i;
%mend;
%test;
This gives a warning: "Apparent symbolic reference NAME not resolved."
I thought the %put statement should resolve to
1st step: &name1 (&&-->& and &i-->1)
2nd step: Tom
It seems that in the first step already, SAS searches for the macro variable &name, although there are two ampersands in front of it.
This is the SAS log, with symbolgen enabled.
WARNING: Apparent symbolic reference NAME not resolved.
SYMBOLGEN: Macro variable I resolves to 1
&&name1
Am I missing something or are there SAS settings that I should check? Thanks in advance!
Restarting the session seems to have solved the problem for you as Quentin commented.
Other options you can try if the same problem persists is turning on symbolgen option and add dot to resolve the macro variable names %put &&name&i..;
I have to revamp a SAS project in which there are macro variables such as the following:
%let myDate = intnx('month',today(),-1);
and later...
data temp;
a = &myDate;
run;
I'm inclined to use %sysfunc instead:
%let myDate = %sysfunc(intnx(month,%sysfunc(today()),-1));
But I'm wondering... is this a matter of preference or is there some sound reason to prefer one method over the other?
For extremely large datasets, you might find that with this particular example, calling intnx once for every row to get the same value leads to poor performance vs. defining a macro variable once and re-using it indefinitely.
Rob Penridge has demonstrated that in this instance the overhead is likely to be negligible, but for more computationally intensive code this will obviously not always be the case. Your mileage may vary.
More generally, when you start storing code rather than just constants within macro variables, you have to start thinking quite carefully about certain things:
What sort of macro quoting might or might not be required (particularly in a SAS/CONNECT environment when you need to rsubmit blocks of code using macro variables)
Whether or not your macro variable contains semicolons or other characters that might cause unexpected interactions with other blocks of code
Whether it contains macro references that you don't want to resolve until the code executes
You also need to consider the question of timing. If you use %sysfunc() then the function runs when the macro variable is created. If you just store the function call in the macro variable then the function actually does not execute until the data step is running. And in this case since it is calling the today() function it will run for every observation. If you start your data step just before midnight on the last day of the month you could end up with different values of A on different observations in the same dataset.
These are two different things that you are talking about. The first code with %LET and DATA step will create a macro variable myDate without executing the INTNX function, but will create a Table with column a
But, the second revamped %LET statement will actually create just a macro variable with value of INTNX function executed.
So, it actually depends on what the business requirement is -
To create a Table
OR
create a Macro variable which can be created once and used used over and over again.
It's a matter of personal preference. Personally I like to use the %sysfunc approach as it allows me to debug/print the result without having to process any datasteps.
Really I'd say whatever allows for better readability and maintainability. If you're working with people that hate using macros then consider using the simpler first approach. Horses for courses.
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.