Get the last value in a macro variable - sas

I create a macro variable
%let param1 = 201601 201602 201603 201604 201605;
I don't know how to get the last value of that variable and assign it to a new variable. How should I do that?

%let last = %scan(&param1, -1) ;
When the second argument is negative, it scans from the right instead of left.

Related

appending a counter to a string obtained by dereferencing macro variable

how do I get so inside the loop I get: var1, var2? I know it does not work to dereference j but the meaning gets more clear to what I want to do (see below)
%let var1 = apple;
%let var2 = pear;
data _null_;
do j=1 to j=2;
put &var&j; //<---?
end;
run;
in the log:
apple
pear
As noted above, J is not a macro variable so you cannot use it as such. You can use the SYMGET function to retrieve the value though. Assuming you want data step logic for some reason:
data _null_;
do i=1 to 2;
x= symget(catt('var', i));
put x;
end;
run;
Sounds like you want to resolve a macro variable whose name you are creating by appending the value of another macro variable to some constant prefix.
If you try to use code like this:
%let var1 = apple;
%let var2 = pear;
%let j=1 ;
%put &var&j;
You will get an error message that the macro variable named VAR does not exist.
You need to signal to the macro processor that it needs to delay trying to evaluate &var until after the suffix has been appended. The way to do this is to double the first &.
%put &&var&j;
The presence of double &'s will cause the macro processor to replace them with a single & and set a reminder to itself the re-scan the result for more macro variable references.
So the first pass will replace && with & and replace &j with 1. Then the second pass will replace &var1 with apple.

Extracting Digits from Year and Concatenation

I have variable x = 2001. I want to produce a string that looks like this:
"TEST0106.xls". The 01 in that string is the last two digits of 2001, and the 06 is the last two digits of 2006 (x+5).
My current code is this:
%let x = 2001;
%let sub1 = %sysfunc(mod(&x, 100));
%let sub2 = %sysfunc(mod(&x+5, 100));
%let test = TEST&sub1&sub2.xls;
%put &test;
However, this just gives me "TEST16xls" since the 0 disappears in the modulus division and I'm not sure why the period isn't there. I believe I'll have to do some approach where I convert the numbers into characters and do a sub-string. How would I go about this task?
First, you don't need to use modulo arithmetic to do a substring in macro variable land, everything is text, so just use %substr.
Second, you can give an optional argument to %sysfunc telling it how to format the result; use the z2 format to tell it what you want. I think leaving the modulo is convenient here simply because it does give you that option. Otherwise you can use %sysfunc(putn( if you want to not use modulo.
Third, you need another . since the first one ends the macro variable (technically macro variables are & to . inclusive unless they hit another character that is invalid for macro variable names first).
%let x = 2001;
%let sub1 = %substr(&x,3,2);
%let sub2 = %sysfunc(mod(&x+5, 100),z2.);
%let test = TEST&sub1.&sub2..xls;
%put &test;
or
%let sub2 = %sysfunc(putn(&sub1.+5,z2.));

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.

Put list of values into several macro variables in SAS

I have a list of values defined in a macro variable, e.g.,
%let datelist = 20100614 20120309 20151215;
Now, I want to put these values into the corresponding number of macro variables. In this case, I want to put them into Date1, Date2, Date3.
Of course, I could manually type out:
%let Date1 = 20100614;
%let Date2 = 20120309;
%let Date3 = 20151215;
How can I do that in a dynamic way so that if there were 25 dates, or 2, it would still work?
Ok, I'll suggest a data step, because I prefer that over macro loops any day.
Use COUNTW() to count the number of loops required and use CALL SYMPUTX to create the macro variables. You should look into the third parameter of the function if you want to control the scope of the macro variable.
%let datelist = 20100614 20120309 20151215;
data _null_;
word = "&datelist";
n=countw(word);
do i=1 to n;
call symputx('date'||Put(i, 8. -l), scan(word, i));
end;
run;
%put &date1.;
%put &date2.;
%put &date3.;

SAS store macro reference in macro variable

I want to store a list of macro references in another macro variable and then change the content of one of the referenced variables.
As example:
%LET String=FirstString;
%LET KeepMacroNotString=&String;
%PUT &String = &KeepMacroNotString ?;
%LET String=String changed;
%PUT &String = &KeepMacroNotString?;
In the end I would like that %PUT &KeepMacroNotString resolves to "String changed". However it sticks to the first assignment.
Any ideas?
Thx, Lubenja
Much easier to do with a data step.
data _null_;
call symputx('KeepMacroNotString','&String');
run;
I found the solution:
A combination of the %NRSTR function and the %UNQUOTE function do the trick:
%LET String=FirstString;
%LET KeepMacroNotString=%NRSTR(&String);
%PUT &String = &KeepMacroNotString ?;
%LET String=String changed;
%PUT &String = %UNQUOTE(&KeepMacroNotString)?;
Explanation: First you have to mask the "&" to prevent the macro from being resolved (%NRSTR()).
But when you want to use the marco, then you have to unquote it again (%UNQUOTE()).
I wouldn't use this approach, but if you don't mind warning messages in the log (I do), you could in theory just change the order of your statements:
WARNING: Apparent symbolic reference STRING not resolved.
57
58 %LET KeepMacroNotString=&String;
59
60 %LET String=FirstString;
61 %PUT &String = &KeepMacroNotString ?;
FirstString = FirstString ?
62
63 %LET String=String changed;
64 %PUT &String = &KeepMacroNotString?;
String changed = String changed?
This is basically an ugly way of accomplishing the same sort of indirection that Tom did more gracefully. Key point being that that the macro variable KeepMacroNotString is given a value of &String, not the resolved value.
In a macro setting, this can also be accomplished by assigning default value as a macro variable reference, e.g.:
59 %macro indirect(String=
60 ,KeepMacroNotString=&string
61 );
62 %put _local_;
63 %put &string = &keepmacronotstring;
64 %mend indirect;
65 %indirect(string=Hi)
INDIRECT KEEPMACRONOTSTRING &string
INDIRECT STRING Hi
Hi = Hi