SAS Macro Variable Numeric But Char in another Macro? - sas

I want to do simple math on numeric date, format YYYYMM, but when I run my monthend_add macro in another macro it changes it to character. Can anyone explain this?
/* macro to add months and keep YYYYMM format */
%Macro monthend_add(me, add);
%let months = %eval(&me / 100 * 12 + %sysfunc(mod(&me, 100)) + &add - 1);
%eval(&months / 12 * 100 + %sysfunc(mod(&months, 12)) + 1);
%mend monthend_add;
%let me = 201512;
%let me = %monthend_add(&me, 1);
%put me: &me, %datatyp(&me);
%macro now_char;
%let me = 201512;
%let me = %monthend_add(&me, 1);
%put me: &me, %datatyp(&me);
%mend;
%now_char

In both cases (global scope in first, inner scope in second) you are returning the semicolon in your monthend_add macro. To fix remove this as follows:
%Macro monthend_add(me, add);
%let months = %eval(&me / 100 * 12 + %sysfunc(mod(&me, 100)) + &add - 1);
%eval(&months / 12 * 100 + %sysfunc(mod(&months, 12)) + 1)
%mend monthend_add;
In the first instance, it seems the returned semicolon terminated your assignment statement. Hence &me=201512 and %datatype(201512)=NUMERIC.
In the second instance (within a macro) it seems this was not the case, and so &me=201512; and %datatyp(201512;)=CHAR
I would guess the reason is that in global context, the macro executes immediately before the assignment is finished (hence terminates itself at the first semicolon).
Whereas the in the second (inner macro) context, the monthend_add macro cannot be run until the now_char is called - hence when now_char is compiled, the space for the value of me is pre-allocated, into which the returned semicolon is then subsequently stored.
As a final note - remember that (technically) SAS macro variables only ever store text! 65,534 characters to be exact.

Related

Prompt or Macro Variables used in calculations

I have created a numeric variable using the Prompt Manager in EG.
This variable is called HYr for the highest year of data that I am pulling.
When running the program I create 4 new variables based on the highest year and this is where I am having issues.
I have the following:
%Let Yr2 = &HYr. - 1;
%Let Yr3 = "&HYr." - 2;
%Let Yr4 = &HYr. - 3;
%Let Yr5 = '&HYr.' - 4;
I am trying to subtract the value from the year and the new variable will be used in determining date ranges that are being pulled. I am trying several things and learning in the process but I am still stuck.
I know it is probably just a simple syntax issue and given enough time I will probably be able to get it but no one in my office has any better SAS skills than I do and that isn't much.
thanks in advance.
Use %EVAL() to do calculations with integers and macro variables.
%let HYR = 2018;
%Let Yr2 = %eval(&HYr. - 1);
%Let Yr5 = %eval(&HYr. - 4);
%put HYR: &hyr;
%put YR2: &yr2.;
%put YR5: &yr5.;
EDIT: If you were trying to do other calculations that included decimals you would need to use %SYSEVALF instead.
%let HYR = 2018;
%Let Yr2 = %sysevalf(&HYr. - 0.1);
%Let Yr5 = %sysevalf(&HYr. - 0.4);
%put HYR: &hyr;
%put YR2: &yr2.;
%put YR5: &yr5.;

Improvement on %NRQUOTE 'removal'

I have the following piece of code that works, but I'd like to know if anyone can come up with a better way of 'removing' the %nrquote. I have had to add a %SUBSTR function, which works, but I'm keen to know if there are any other suggestions, and if anyone can help explain why the code doesn't work without the %let statement within the mvar macro definition.
/* Automatically generated by DI Studio - cannot change */
%let _where_clause = %nrquote(name = %'Henry%');
%let _mac1 = %nrquote(lemk);
%let _variable = weight;
%let _input0 = sashelp.class;
/* End of auto-generated code */
options mprint;
%macro mvar;
%if &_where_clause ^= %then %do;
/* Re-assign the _where_clause variable to 'remove' %nrquote */
%let _where_clause = %substr(&_where_clause,1);
where &_where_clause
%end;
%mend mvar;
proc sql;
select &_variable into :&_mac1
from &_input0
%mvar
;
quit;
Without the %let statement, the code fails with this error:
NOTE: Line generated by the macro variable "_WHERE_CLAUSE".
1 name = 'Henry'
-
22
MPRINT(MVAR): where name = '
NOTE: Line generated by the macro variable "_WHERE_CLAUSE".
1 name = 'Henry'
-
200
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string,
a numeric constant, a datetime constant, a missing value, (, *, +, -, ALL, ANY,
BTRIM, CALCULATED, CASE, INPUT, PUT, SELECT, SOME, SUBSTRING, TRANSLATE, USER.
ERROR 200-322: The symbol is not recognized and will be ignored.
114 ;
MPRINT(MVAR): Henry'
You need %UNQUOTE which is what is happening with %LET, it is un-quoting the quoted quotes.
Change
where &_where_clause
to
where %unquote(&_where_clause)

use a variable as a macro variable data step

I'm looking for a way to use a normal variable value as a macro variable in a data step.
For example I have macro variable &statesList_Syphilis = AAA
and another macro variable &statesList_Giardia = BBB
And in a data step I have a variable Germ wich contains 2 rows: "Syphilis" and "Giardia".
In my data step I need to find AAA when iterating over the first row when Germ="Syphilis"
and BBB when iterating over the second row, when Germ="Giardia"
an attempt would look like this
%let statesList_Syphilis = AAA;
%let statesList_Giardia = BBB;
data test;
set mytablewithgerms; * contains variable Germ ;
* use germ and store it in &germ macro variable ;
* something like %let germ = germ; or call symput ('germ',germ);
* I want to be able to do this;
xxx = "&&statesList_&germ"; * would give xxx = "AAA" or xxx = "BBB";
* or this;
&&statesList_&germ = "test"; * would give AAA = "test" or BBB = "test";
run;
I don't think this is possible, but I figured I would ask just to be sure.
Thanks!
EDIT (Following questions in the comments, I'm adding context to my specific problem, but I feel this is making things more complicated):
This was an attempt to simplify the problem.
In reality AAA and BBB are long lists of words
like
"asymptomatic_1 fulminant_1 chronic_1 chronic_1 fatalFulminant_1 hepatocellular_1 compensated_1 hepatocellular_2 decompensated_1 fatalHepatocellular_1 fatalHepatocellular_2 fatalDecompensated_1"
And I don't want to store this long string in a variable, I want to iterate each word of this string in a do loop with something like:
%do k=1 %to %sysfunc(countw(&&statesList_&germ));
%let state = %scan(&&statesList_&germ, &k);
* some other code here ;
%end;
EDIT2:
here is a more complete view of my problem:
%macro dummy();
data DALY1;
* set lengths ;
length Germ $10 Category1 $50 Category2 $50 AgeGroupDALY $10 Gender $2 value 8 stateList$999;
* make link to hash table ;
if _n_=1 then do;
*modelvalues ----------------;
declare hash h1(dataset:'modelData');
h1.definekey ('Germ', 'Category1', 'Category2', 'AgeGroupDALY', 'Gender') ;
h1.definedata('Value');
h1.definedone();
call missing(Germ, Value, Category1, Category2);
* e.g.
rc=h1.find(KEY:Germ, KEY:"ssssssssss", KEY:"ppppppppppp", KEY:AgeGroupDALY, KEY:Gender);
*states ---------------------;
declare hash h2(dataset:'states');
h2.definekey ('Germ') ;
h2.definedata('stateList');
h2.definedone();
end;
set DALY_agregate;
put "°°°°° _n_=" _n_;
DALY=0; * addition of terms ;
rc2=h2.find(KEY:Germ); * this creates the variable statesList;
put "statesList =" statesList;
* here i need statesList as a macro variable,;
%do k=1 %to %sysfunc(countw(&statesList)); *e.g. acute_1 asymptomatic_1 ...;
%let state = %scan(&statesList, &k);
put "=== &k &state";
&state = 1; * multiplication of terms ;
* more code here;
%end;
run;
%mend dummy;
%dummy;
EDIT3:
The input dataset looks like this
Germ AgeGroup1 AgeGroup2 Gender Cases Year
V_HBV 15-19 15-19 M 12 2015
V_HBV 15-19 15-19 M 8 2016
V_HBV 20-24 20-24 F 37 2011
V_HBV 20-24 20-24 F 46 2012
V_HBV 20-24 20-24 F 66 2013
The output dataset will add variables contained in the string defined by the macro variable which depends on the Germ.
e.g. for V_HBV it will create these variables: asymptomatic_1 fulminant_1 chronic_1 chronic_1 fatalFulminant_1 hepatocellular_1 compensated_1 hepatocellular_2 decompensated_1 fatalHepatocellular_1 fatalHepatocellular_2 fatalDecompensated_1
I'm not following the big picture, but one of the previous iterations of your question had some code (pseudo code) that illustrates possible confusion about how the macro language works. Consider this step:
data _null_;
germ="Syph";
call symput('germ',germ);
%let Germ=%sysfunc(cats(germ));
put "germ = &germ";
run;
%put &germ;
The log from executing that in a fresh SAS session shows:
1 data _null_;
2 germ="Syph";
3 call symput('germ',germ);
4 %let Germ=%sysfunc(cats(germ));
5 put "germ = &germ";
6 run;
germ = germ
7 %put &germ;
Syph
Now let's talk about what's happening. I'll use the line numbers from the log.
Line 2 assigns text string Syph to data step variable germ. Nothing special.
Line 3 creates a macro variable named Germ, and assigns in the value of the datastep variable germ. So it assigns it the value Syph. This CALL SYMPUT statement executes when the data step executes.
Line 4 is a macro %let statement. It creates a macro variable named Germ, and assigns it the value germ. Because this is a macro statement, it executes before any of the DATA STEP code has executed. It does not know about data step variables. Line 4 is equivalent to %let Germ=germ. To the macro language, the right hand side is just a four-character string germ. It is not the name of a data step variable. %syfunc(cats()) is doing nothing, because there is no list of items to concatenate.
Line 5 is a data step PUT statement. The macro reference &germ is resolved while the data step is compiling. At this point the macro variable germ resolves to Germ because the %LET statement has executed (the CALL SYMPUT statement has not executed yet).
Line 7 is a %PUT statement that executes after the DATA NULL step has completed (and after the CALL SYMPUT has written the value Syph to macro variable Germ).
As a general principle, it is difficult (and unusual) to have a single data step in which you are using data to create a macro variable (e.g. via call symput) and using that macro variable in the same step (i.e. referencing the macro variable). Macro references are resolved before any of the data step code executes.
Typically if your data are already in a dataset, you can get what you want with data step statements (DO loops rather than %DO loops, etc). Or alternatively you can use one DATA step to generate your macro variables, and a second DATA step can reference them.
Hope that helps.

Intermediate variables in a SAS macro

Here's my macro, below. The variables var1, var2, var3, VR, and maxwt are not input to the macro, and are not on a file. They are intermediate results which are calculated inside the macro. When I execute in in SAS 9.3, I get a message like this:
1 var1 = &dcount1 * (&spop1/&refpr1)**2;
----
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
Macro:
%macro confi (aart,dcount1,dcount2,dcount3,spop1,spop2,spop3,refpr1=0.53468238,refpr2=0.30153350,refpr3=0.16378412);
var1 = &dcount1 * (&spop1/&refpr1)**2;
var2 = &dcount1 * (&spop2/&refpr2)**2;
var3 = &dcount3 * (&spop1/&refpr3)**2;
VR = var1+var2+var3;
maxwt = max(&refpr1 &refpr2 &refpr3);
CI_low = (VR/ 2*&aart)) * cinv(0.025,2*(&aart**2)/VR);
CI_high = (( VR + maxwt**2) / (2*(&aart+maxwt)))* cinv(0.975, 2*(&aart+maxwt)**2/(VR + maxwt**2));
%put &aart CI_low CI_high;
%mend confi;
%confi (aart=1000, dcount1=20, dcount2=70, dcount3= 10. spop1=3000, spop2=3000, spop3=200);
That macro would have to be executed inside of a data step. Those are data step variables and statements and can't be executed in open code.
You also can't use %put for that purpose - you need to use put.
You are missing the datastep to calculate all your non-macro variables.
When you call a MACRO to execute, it is a BEST PRACTICE not to put ; at the end of the line.
On the CI_LOW arithmetic operation you have mismatch parenthesis.
When you call the macro %confi, you are assigning dcount3 variable value wrong, you need a comma, not a dot.
When you declare a Macro, you don't assign values there, only declare variables.
On the Max Function, the variables need to be separated by commas.
Try this:
Options Macrogen Symbolgen;
%macro confi (aart,dcount1,dcount2,dcount3,spop1,spop2,spop3,refpr1,refpr2,refpr3);
data _null_;
var1 = &dcount1 * (&spop1/&refpr1)**2;
var2 = &dcount1 * (&spop2/&refpr2)**2;
var3 = &dcount3 * (&spop1/&refpr3)**2;
VR = var1+var2+var3;
maxwt = max(&refpr1, &refpr2, &refpr3);
CI_low = (VR/ 2*&aart) * cinv(0.025,2*(&aart**2)/VR);
CI_high = (( VR + maxwt**2) / (2*(&aart+maxwt)))* cinv(0.975, 2*(&aart+maxwt)**2/(VR + maxwt**2));
put 'low =' CI_low;
put 'high = ' CI_high;
run;
%put &aart;
%mend confi;
%confi (aart=1000, dcount1=20, dcount2=70, dcount3= 10, spop1=3000, spop2=3000, spop3=200, refpr1=0.53468238, refpr2=0.30153350, refpr3=0.16378412)

How to create new macro variable from existing macro variables using calculations in SAS?

I'd like to create a new macro variable from other macro variables that already exist.
I have tried multiple variations of call symput, %eval , and input to no avail...
I would like d to evaluate to 3 / 30 = .10.
***** taken directly from the sas help files... ;
%let a=1+2;
%let b=10*3;
%let c=5/3;
%let eval_a=%eval(&a);
%let eval_b=%eval(&b);
%let eval_c=%eval(&c);
%put &a is &eval_a;
%put &b is &eval_b;
%put &c is &eval_c; * not sure why this evaluates to 1, but I'm sure it's documented somewhere... ;
***** This evaluates to 0...
%let d = %eval(%eval(&a) / %eval(&b)) ;
%put &d ;
Thanks so much...
%eval will only return an integer. To get the decimal, you need to use %sysevalf.