Unable to execute Sas INTCK in macro - date

I'm running the following macro:
%macro diff(yymm);
%let date1=%sysfunc(inputn(strip(putn(&yymm.,yymmn6.))||'01',yymmdd10.));
%let date2=%sysfunc(inputn(strip(putn(201811,yymmn6.))||'01',yymmdd10.));
%let j=%sysfunc(intck(month,&date1.,&date2.));
%put &date1. &date2. &j.;
%mend;
%diff(201807);
%diff(201808);
Basically to find the difference in months from Nov'18 to whichever date I pass as argument to this macro. I'm not sure where am I going wrong, but I get this following error:
Argument 1 to function INPUTN referenced by the %SYSFUNC or %QSYSFUNC macro function is out of range.
NOTE: Mathematical operations could not be performed during %SYSFUNC function execution. The result of the operations have been set
to a missing value.
Can somebody please help me here?

The YYMMN6. informat is not going to recognize strip( as a valid date value. You cannot call functions in macro code without wrapping each function in the %sysfunc() macro function, they will just look like letters to the macro processor.
Is this what you are trying to do?
%macro diff(yymm);
%put &yymm 201811 %sysfunc(intck(month,%sysfunc(inputn(&yymm, yymmn6.)),'01NOV2018'd));
%mend;
Result:
1104 %diff(201807);
201807 201811 4
1105 %diff(201808);
201808 201811 3

You're overcomplicating the string to date conversion. You've already got the string in format YYMMN6., you just need to get the actual date value from that string.
%macro diff(yymm);
%let date1 = %sysfunc(inputn(&yymm, yymmn6.));
%let date2 = %sysfunc(inputn(201811, yymmn6.));
%let j=%sysfunc(intck(month,&date1.,&date2.));
%put &date1. &date2. &j.;
%mend;
%diff(201807);
%diff(201808);

Assuming your dates are consistently in YYYYMM format then, couldn't you simply use something like this:
%macro diff(yymm);
%put %eval((%substr(&yymm, 1,4) - 2018)*12 + (%substr(&yymm, 5,2) - 11));
%mend;

Related

Unable to convert argument to date format

So I have a macro here which takes sas month in yymmn6 format to compute the next 2 months.
%macro pull(yymm);
%let month1=%sysfunc(inputn(&yymm.,yymmn6.));
%let month2=%sysfunc(inputn(intnx('month',&month1.,1),yymmn6.));
%let month3=%sysfunc(inputn(intnx('month',&month1.,2),yymmn6.));
%put &month1 &month2 &month3;
%mend;
%pull(201807);
I'm not being able to understand the error in the code because I get the following warning:
Argument 1 to function INPUTN referenced by the %SYSFUNC or %QSYSFUNC macro function is out of range.
When I check the log, &month1 comes as 21366 which should ideally have been 201807. Can somebody please help me out here?
Every function needs its own %SYFUNC() wrapper so your code is passing the literal string INTNX( to INPUTN() function. Also you don't need to add quotes around string values in macro code. So remove the quotes around MONTH.
So first convert your YYYYMM string into a date. Then pass that date value to the INTNX() function. You can use the optional second parameter of the %SYSFUNC() function to specify what format to use when converting the function result into a string.
%macro pull(yymm);
%local date;
%let date=%sysfunc(inputn(&yymm,yymmn6));
%let month1=%sysfunc(intnx(month,&date,0),yymmn6.);
%let month2=%sysfunc(intnx(month,&date,1),yymmn6.);
%let month3=%sysfunc(intnx(month,&date,2),yymmn6.);
%put &month1 &month2 &month3;
%mend;
%pull(201807);
Results:
201807 201808 201809

Multiple values for a sas macro parameter

I'm new to SAS Macro programming and need to enable the following macro to be able to handle and process multiple values for its macro parameters.Hello,
data have;
input name $ ACCOUNT_ID $ cust_id;
cards;
ARTHUR CC1234 1234
TOM eil1235 1235
MIKEZ tb1236 1236
MATT mb1237 1237
LIZ TB1238 1238
PIZ VB1239 1239
TAN MB1240 1240
PANDA . 1241
;
run;
%MACRO algo (IN_DS=,VAR_LIST=,DATA_TYPE_LIST=,OUT_DS=);
DATA &OUT_DS;
SET &IN_DS;
%If &data_type_LIST = num %then
&var_LIST=sum(&VAR_LIST,2);
%else &var_LIST=cats(&var_LIST,'re');;
run;
%mend;
%algo(IN_DS=HAVE,VAR_LIST=CUST_ID,DATA_TYPE_LIST=num,OUT_DS=out1);`
I now need to enable this macro to be able to pass multiple values for the macro parameters. Something like this :
%algo(IN_DS=HAVE,VAR_LIST='CUST_ID,ACCT_ID',DATA_TYPE_LIST='num,char',OUT_DS=out1);
Can someone help me enable this functionality in the macro code.
The parameter argument should be macro quoted with %STR() in the macro invocation.
Try
%algo
( IN_DS=HAVE
, VAR_LIST= %STR (CUST_ID, ACCT_ID)
, DATA_TYPE_LIST=num
, OUT_DS=out1
);
Macro quoting is different than DATA step quoting used for character literals.
Make sure the macro can handle multiple values. In general it is not a good idea to use comma as the delimiter in your list of values when calling a macro.
Usually space is the best delimiter since then you can use the macro value directly in the generated code. For example if your variables are all of the same type you can just use data step ARRAY.
%MACRO algo (IN_DS=,VAR_LIST=,DATA_TYPE_LIST=,OUT_DS=);
DATA &OUT_DS;
SET &IN_DS;
array list &var_list ;
do _n_=1 to dim(list);
%if &data_type_LIST = num %then %do ;
list(_n_)=sum(list(_n_),2);
%end;
%else %do;
list(_n_)=cats(list(_n_),'re');
%end;
end;
run;
%mend algo;
If your variables are NOT all of the same type then you need to generate a separate statement for each variable. In that case you can use a different delimiter if you want, like a pipe character, that is easier to use as delimiter in calls to macro functions like %scan().
%MACRO algo (IN_DS=,VAR_LIST=,DATA_TYPE_LIST=,OUT_DS=);
%local i var;
DATA &OUT_DS;
SET &IN_DS;
%do i=1 %to %sysfunc(countw(&var_list,|));
%let var=%scan(&var_list,&i,|);
%if %scan(&data_type_LIST,&i,|) = num %then %do ;
&var=sum(&var,2);
%end;
%else %do;
&var=cats(&var,'re');
%end;
%end;
run;
%mend algo;
%algo(IN_DS=HAVE,VAR_LIST=CUST_ID|ACCT_ID,DATA_TYPE_LIST=num|char,OUT_DS=out1);
If you want to pass a list of variables and then use that list in the code you posted above, my suggestion would be to treat the &var_list as a list, and use scan to determine how many variables there are, and then loop through the list and execute the code accordingly.

How to pass macro variable into macro variable name in SAS ?

%let val = ' run';
%macro rrun;
%put successfully run;
%mend;
%macro x;
%r%cmpres(&val.);
/* %rrun;*/
%mend;%x
I"m trying to pass macro variable into macro variable name in SAS. to run %rrun using %cmpres(&val.) passing value to macro name "rrun" I don't understand why this doesn't work. when compress &val is "run"
How can I solve this and still passing &val ?
Thanks,
Why do you have quotes in your macro variable?
166 %let val = ' run';
167 %put |%cmpres(&val)|;
|' run'|
If you want to dynamically generate the name of macro to call it works much easier if you first generate the name into a macro variable and then reference that macro variable. Otherwise you risk confusing the tokenizer.
%macro x;
%local mname;
%let mname=r%cmpres(&val);
%&mname;
%mend;
%x

sas macros for incrementing date

My codes are:
libname " Cp/mydata"
options ;
%let yyyymmdd=20050210;
%let offset=0;
%let startrange=0;
%let endrange=0;
/* MACRO FOR INCREMENTING THE DATE */
%macro base(yyyymmdd=, offset=);
%local date x ds; /* declare macro variables with local scope */
%let date=%sysfunc(mdy(%substr(&yyyymmdd,5,2)
,%substr(&yyyymmdd,7,2)
,%substr(&yyyymmdd,1,4))); /* convert yyyymmdd to SAS date */
%let loopout=100;/* hardcoded - number of times to check whether ds exists */
%do x=&offset %to &loopout; /* begin loop */
/* convert &date to yyyymmdd format */
%let ds=AQ.CO_%sysfunc(intnx(day,&date,&offset),yymmddn8.);
%if %sysfunc(exist( &ds )) %then %do;
%put &ds exists!;
&ds /* write out the dataset, if it exists */
%let x=&loopout; /* exit loop */
%end;
%else %do;
%put &ds does not exist - checking subsequent day;
%let date=&date+1;
%end;
%end;
%mend;
%macro loop(yyyymmdd=, startrange=, endrange=);
%local date x ds;
%let date=%sysfunc(mdy(%substr(&yyyymmdd,5,2)
,%substr(&yyyymmdd,7,2)
,%substr(&yyyymmdd,1,4)));
data x;
set set %base(yyyymmdd=&yyyymmdd, offset=0)
/* loop through each specific dataset, checking first whether it exists.. */
%do x=&startrange %to &endrange;
%let ds=AQ.CO_%sysfunc(intnx(day,&date,&x),yymmddn8.);
%if %sysfunc(exist( &ds )) %then %do;
&ds
%end;
%end;
;
run;
%mend;
This was the error generated when I tried to run this macro.
data temp;
58 set %loop(yyyymmdd=&yyyymmdd, startrange=&startrange,
58 ! endrange=&endrange);
ERROR: File WORK.DATA.DATA does not exist.
ERROR: File WORK.X.DATA does not exist.
AQ.CO_20050210 does not exist - checking subsequent day
AQ.CO_20050211 does not exist - checking subsequent day
AQ.CO_20050212 exists!
NOTE: The system stopped processing this step because of errors.
I want help on two things:
1) Here, I'm trying to increment my date by 1 or 2 or so on if that date is not there in my original dataset. Please help to make this macro work fine.
2)I would like to have another column ie work.date in my data that will have 0 or 1(1 if the specified date yyyymmdd exist in our original data and 0 if I'm incrementing). Please make the specified changes in my macro.
Thanks in advance!!
I wasn't quite sure exactly what your %base() macro was trying to achieve but there were a couple of things I noticed.
First try turning on option mprint; to help with debugging. If you still need more debugging info you can also try turning on the following options (I'd suggest 1 at a time until you know which ones you need):
option symbolgen macrogen mlogic;
Secondly, you have set set instead of just set in your example code. I don't think that is helping any =).
When I tried the code quickly on my machine I noticed I was getting a strange error (different from yours) when I called the %base() macro. It seemed like an error that shouldn't be occurring so I wrapped the call in an %unquote() function just to make sure and I started to receive the error your post mentioned. You may want to try this as well:
set %unquote(%base(yyyymmdd=&yyyymmdd, offset=0))
Normally the %unquote() function isn't required unless you are explicitly using macro quoting functions and getting strange errors, but SAS macros sometimes seem to have a mind of their own. I only ever add this when I know it is required.
Also, your libname call is missing a semicolon at the end of the line.
Finally, some advice on working with dates in the SAS macro language. Don't keep converting between the date value, and the formatted value. It will make your code bigger, more error prone and more difficult to read. I know because I used to do it that way too. Try instead to always work with variables that contain the actual date value (by using the result from %sysfunc(mdy()) ) and then if you need a formatted value then create a new variable (eg. %let yyyymmdd = %sysfunc(putn(&mydate),yymmddn8.);. When you pass values from one macro to another, don't pass the formatted values even if it seems easier, pass the actual values.
Making the above changes removed all errors on my machine. Final code:
libname " Cp/mydata";
%let yyyymmdd=20050210;
%let offset=0;
%let startrange=0;
%let endrange=0;
/* MACRO FOR INCREMENTING THE DATE */
%macro base(yyyymmdd=, offset=);
%local date x ds; /* declare macro variables with local scope */
%let date=%sysfunc(mdy(%substr(&yyyymmdd,5,2)
,%substr(&yyyymmdd,7,2)
,%substr(&yyyymmdd,1,4))); /* convert yyyymmdd to SAS date */
%let loopout=100;/* hardcoded - number of times to check whether ds exists */
%do x=&offset %to &loopout; /* begin loop */
/* convert &date to yyyymmdd format */
%let ds=AQ.CO_%sysfunc(intnx(day,&date,&offset),yymmddn8.);
%if %sysfunc(exist( &ds )) %then %do;
%put &ds exists!;
&ds /* write out the dataset, if it exists */
%let x=&loopout; /* exit loop */
%end;
%else %do;
%put &ds does not exist - checking subsequent day;
%let date=&date+1;
%end;
%end;
%mend;
%macro loop(yyyymmdd=, startrange=, endrange=);
%local date x ds;
%let date=%sysfunc(mdy(%substr(&yyyymmdd,5,2)
,%substr(&yyyymmdd,7,2)
,%substr(&yyyymmdd,1,4)));
data x;
set %unquote( %base(yyyymmdd=&yyyymmdd, offset=0))
/* loop through each specific dataset, checking first whether it exists.. */
%do x=&startrange %to &endrange;
%let ds=AQ.CO_%sysfunc(intnx(day,&date,&x),yymmddn8.);
%if %sysfunc(exist( &ds )) %then %do;
&ds
%end;
%end;
;
run;
%mend;
%loop(yyyymmdd=&yyyymmdd, startrange=&startrange, endrange=&endrange);
Seems to me that your solution is quite complex.
But i believe that at least one issue is the variable x in our second macro (%loop): i do not see where you define it.
You can probably do all of this much easier, IF you do not need to limit the loopout. If you just want all datasets beyond the offset, you can simplify all this by making use of the SASHELP library to find the datasets you need. And then just loop over that result.
DEPRECATED REPLY BELOW, misread the need
You are partially reinventing the wheel, have a deeper look at the intnx and intck functions.
http://support.sas.com/documentation/cdl/en/etsug/60372/HTML/default/viewer.htm#etsug_tsdata_sect038.htm
https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212700.htm

Attribute Change of Macro Variable in SAS

I have a macro variable like &a having value of (1234.45)*. I am trying to replace the ( and ) from the macro and replace them with a negative mark since its a negative number.
%let a=(1234.45)
Some of the options which I have appplied are
%macro test1;
%if %substr(&a,1,1) = '(' %then %do;
%let b=%substr(&a,1,'-')
%end;
%mend;
%test1
This is numeric conversion and best handled in a data step. If for some reason you really need a macro variable, use SYMPUT.
%let a=(1234.45);
data _null_;
x=input("&a.",comma10.);
call symputx("b",x);
run;
%put &=a &=b;