Calling macro variables in SAS - sas

I'm working with SAS enterprise 7.1 and I would like to know if there's any difference between calling a macro variable with &var. vs &var, that is, without the final dot. In general, I see that both methods work, but I would like to know if there's something that I'm missing. Thanks in advance!

For consistency it's best to always finish with a ".", I reckon.
Indeed, the need is not always there, but the use case outlined above hopefully explains why "." is sometimes needed :)

Consider the scenario below:
%let myVar = Demo;
libname example "/home/myName/&myVar85";
SAS will look for the macro variable &myVar85 here. But my macro variable is only myVar....so adding a period tells SAS this is the end of the macro variable and to resolve it.
libname example "/home/myName/&myVar.85";
If you were using it as part of the file path remember to add an extra . for the extension.
ods excel file="/home/myName/&myVar..xlsx" style=meadow;
proc print data=sashelp.class;
run;
ods excel close;

Related

SAS - Dynamically creating macro variable via concatenation of text to a current macro variable

I'm looking to create a macro variable. The macro variable name needs to be made by concatenating text to an already existing variable after it has resolved. Specifically, I want to do this in a PROC SQL block using INTO:. Here's a snippet to explain what I want to do.
%macro MyMacro(process);
PROC SQL;
SELECT
COUNT(*) INTO: &process._new_text
FROM
DataSetHere
;QUIT;
%mend MyMacro;
If I call on this macro and I pass the word "cat" into process, I want to have now declared/initialized a variable with the name cat_new_text, and it should return the COUNT(*) selected in that query whenever &cat_new_text is referenced.
I've done a little reading around, looked into using multiple ampersands, trying to resolved &process within quotes first - nothing has really solved my exact problem. Does anyone know of a clear cut way to accomplish this?
Thanks in advance!
Your code seems fine and it seems like it would do exactly what you describe.
However if your trying to "access" that new macro variable outside the macro, e.g.,
%MyMacro(cat);
%put &cat._new_text.;
then indeed it will not work because the variable was created locally in your macro and does not exist outside the scope of that macro.
To fix that, you simply need to add a %global statement in your macro definition:
%macro MyMacro(process);
%global &process._new_text;
PROC SQL;
SELECT COUNT(*)
INTO: &process._new_text
FROM DataSetHere
;
QUIT;
%mend MyMacro;

SAS PROC MEANS with selected columns to mean

proc means data=tableepisodes noprint;
output out=tableepisodes
mean(%ratings %dummies)=%ratings %dummies;
by ProgCodeID ProgSeasonCodeID year week
I was reading through a SAS code and I am not sure what the mean part of the code does ,
Is it that it only takes the mean of %ratings variables and attach the % dummies variables to the output ?
would really appreciate if I could get help in understanding this code snippet
That isn't a complete code snippet, and no.
It calculates the mean of the variables listed in %rating AND %dummies, assuming of course that's what is included in those macros.
Without seeing the macro definitions we can't be sure of what it is actually doing.
As written, the code is going to evaluate the means of the variables stored inside the macro variables ratings and dummies. Taking ratings as an example, we're assuming it was defined earlier on as something like:
%let ratings = good bad ugly;
So, when you pass it through the proc means, %ratings will evaluate to good bad ugly and SAS will take the means of all three variables.
You could have written the proc means function as:
proc means data = tableepisodes noprint;
by ProgCodeID ProgSeasonCodeID year week;
var good bad ugly;
output out = tableepisodes mean= / autoname;
run;
instead. (Also, note that you're overwriting your original dataset here, which you may want to avoid.)

SAS dynamically declaring macro variable

My company just switched from R to SAS and I am converting a lot of my R code to SAS. I am having a huge issue dynamically declaring variables (macro variables) in SAS.
For example one of my processes needs to take in the mean of a column and then apply it throughout the code in many steps.
%let numm =0;
I have tried the following with my numm variable but both methods do not work and I cannot seem to find anything online.
PROC MEANS DATA = ASSGN3.COMPLETE mean;
#does not work
&numm = VAR MNGPAY;
run;
Proc SQL;
#does not work
&numm =(Select avg(Payment) from CORP.INV);
quit;
I would highly recommend buying a book on SAS or taking a class from SAS Training. SAS Programming II is a good place to start (Programming I if you have not programmed anything else, but that doesn't sound like the case). The code you have shows you need it. It is a complete paradigm shift from R.
That said, try this:
proc sql noprint;
select mean(payment) into :numm from corp.inv;
quit;
%put The mean is: &numm;
Here's the proc summary / data step equivalent:
proc summary data = corp.inv;
var payment;
output out = inv_summary mean=;
run;
data _null_;
set inv_summary;
call symput('numm',payment);
run;
%put The mean is: &numm;
Proc sql is a more compact approach if you just want a simple arithmetic mean, but if you require more complex statistics it makes sense to use proc summary.

How does SAS macro quoting interact with format literals?

Executing locally in a clean session:
%let x = %str(put(age, best.));
proc sql;
select &x from sashelp.class;
quit;
This generates the following error:
1 put(age, best.)
----
22
----
76
ERROR 22-322: Syntax error, expecting one of the following: a format name, ?.
ERROR 76-322: Syntax error, statement will be ignored.
But this "manually-resolved" version runs without notes, warnings or errors:
proc sql;
select put(age, best.) from sashelp.class;
quit;
Can somebody explain exactly what %str() is doing in this program that causes an issue at execution time? Apologies for the vague question, but I am unsure what the relevant interactions are; I cannot replicate using equivalent data-step syntax so perhaps proc SQL peculiarities are involved?
The %str() function masks a character string during macro compilation. Remove the %str() function in the let statement or add an %unquote() function in the sql select to have if resolve correctly.
Answered at this question on runsubmit.com:
I'm going to mark this answer as
correct because it led me to this page
of documentation:
http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#tw3514-unquote.htm
- "In rare cases, masking text with a macro quoting function changes the way
the word scanner tokenizes the text
... The word scanner does not use it
as the boundary of a literal token in
the input stack". Sounds like a bug,
frankly, but if the tokenizer
algorithm is as ancient and hairy as I
imagine, I'd spin it as a quirk too!
Can you use a format statement instead? For example, this works just fine.
%let x = %str( age format=best.);
proc sql;
select &x. from sashelp.class;
quit;
For some reason SAS doesn't like the "best." format.
i.e. when I try this, your code works
%let x = %str(put(age, 8.));
????
If you add this to your code
%put _user_ ;
you will see how &x is quoted by %str, in the log. That is why the proc sql code doesn't work. Using %Unquote in the select portion of the proc sql statement will allow the code to run.
http://www2.sas.com/proceedings/forum2007/152-2007.pdf

What's your best trick to break out of an unbalanced quote condition in BASE SAS?

As a base SAS programmer, you know the drill:
You submit your SAS code, which contains an unbalanced quote, so now you've got not only and unclosed quote, but also unclosed comments, macro function definitions, and a missing run; or quit; statement.
What's your best trick for not having those unbalanced quotes bother you?
As for myself, I usually Google for "SAS unbalanced quote", and end up with submitting something like this:
*); */; /*’*/ /*”*/; %mend;
... to break out of unclosed comments, quotes and macro functions.
enterprise guide 3 used to put the following line at the top of its automatically generated code:
*';*";*/;run;
however, the only way to really "reset" from all kinds of something unbalanced problems is to quit the sas session, and balance whatever is unbalanced before re-submitting the code. Using this kind of quick (cheap?) hacks does not address the root cause.
by the way, ods _all_ close; closes all the ods destinations, including the default, results destination. in an interactive session, you should open it again with ods results; or ods results on; at least according to the documention. but when i tested it on my 9.2, it did not work, as shown below:
%put sysvlong=&sysvlong sysscpl=&sysscpl;
/* sysvlong=9.02.01M0P020508 sysscpl=X64_VSPRO */
ods _all_ close;
proc print data=sashelp.class;
run;
/* on log
WARNING: No output destinations active.
*/
ods results on;
proc print data=sashelp.class;
run;
/* on log
WARNING: No output destinations active.
*/
Here is the one I use.
;*';*";*/;quit;run;
ODS _ALL_ CLOSE;
QUIT; RUN;
I had a situation with unbalanced quotes in a macro and the only solution was to close the instance of SAS and start over.
I feel that's an unacceptable flaw in SAS.
However, I used the methods by BOTH #2 and #5 and it worked. #2 first and then #1. I put them above ALL code, including my code header, explaining what this program was doing.
Worked like a charm.
I wrote a perl program that reads through any given SAS program and keeps track of things that should come in pairs. With things like parentheses, which can be embedded, it prints the level of nesting at the beginning of every line. It needs to be able to distinguish parentheses that are part of macro functions from those that are part of data step functions, including %sysfunc calls that reside in the macro environment but make calls to data step functions (must also do similar for %syscall macro function invocations), but that is doable through regular expressions. If the level of nesting goes negative, it is a clue that the problem may be nearby.
It also starts counting single and double quotes from the start of the program and identifies whether the count of each such symbol it encounters is odd or even. As with parentheses, it needs to be able to distinguish quotes that are part of macro code from those that are part of data step code and also those that are part of literal strings such as O'Riley and %nrstr(%'%") and not count them, but pattern matching can handle that too.
If the problem of the mismatched item stems from code that is generated at runtime by macro code and is therefore not present in the source program, then I turn on option mfile to write the generated data step code to a file and then run the perl script against that code.
I chose perl because of its strong pattern-matching capabilities but any other pattern-matching language should work fine. Hope this helps.
This works nearly every time for me:
; *'; *"; */;
ODS _ALL_ CLOSE;
quit; run; %MEND;
data _NULL_; putlog "DONE"; run;
You could always just issue a terminate submitted statements command and resubmit what you're trying to run.
just wanted to reiterate AFHood's suggestion to use the ODS _ALL_ CLOSE; statement. That's a key one to include. And make sure you use it every time you're finished with ODS anyway.
Closing the SAS Session worked in my case. I think you can try this once before you try other methods mentioned here.
Yes, I believe the official SAS documentation recommends the solution you have proposed for yourself.