Include macro in a file name - sas

In SAS I like to make a dynamic filename since I am browsing my data on a daily tabel.
I have tried to include a macro in the filename like this:
%let date=input(put(today()-3,ddmmyy6.),6.); *This is equal to todays date-3 (format = 190317)
filename nlp "DailyB.DG%date";
It does not work can you help me?
To get a intuition of what I like to do I have posted a example below
I want to have a date minus 3 days from today in this format:DDMMYY (190317)
So if i run the code the day after it would be 200317.
The variable should then be put into the code so I get the following:
filename nlp 'DailyB.DG190317';

If you want a macro variable to resolve a function, you need %sysfunc. Here's one way to do that.
%let date=%sysfunc(putn(%eval(%sysfunc(today())-3),ddmmyyn6.)); *This is equal to todays date-3 (format = 190317);
%put &=date;
The first %sysfunc asks for the result of today(), the second asks for the result to be formatted. %eval is needed to subtract 3 from the value, as #Quentin points out in comments.
Alternately, call symputx would work here if you're more comfortable in the data step.
data _null_;
call symputx('date',put(today()-3,ddmmyyn6.));
run;
%put &=date;

So what you currently tried is going to end up with a filename statement like
filename nlp "DailyB.DGinput(put(today()-3,ddmmyy6.),6.)";
To run functions in macro code you need to use %SYSFUNC(). You will also need to use the PUTN() function since the PUT() function doesn't work with %SYSFUNC().
%let date=%sysfunc(putn(%sysfunc(today())-3,ddmmyyn6));
filename nlp "DailyB.DG&date";
If you can get the process changed I would recommend using 8 characters for the date part of the name so that you can include the century. Also if you use Year,Month,Day order for your date string your generated filenames will sort in proper date order and also prevent users confusing Columbus Day for Dewey Decimal System Day.

You need to use the %sysfunc() and %eval() macro functions to evaluate the Data Step functions outside the Data Step. As Joe says:
%let date=%sysfunc(putn(%eval(%sysfunc(today())-3),ddmmyyn6.));
Then you need to change your % to a &
filename nlp "DailyB.DG&date";

Related

Issue Creating a Table based on Macro's Parameter Name

So I will briefly explain my code structure before I dive into the issue.
I've a macro
%Sales (Outdata= , dt =, Outdata2= , Outdata3= );
(
I create a table &outdata by (Select * from XYZ);
Proc SQL;
Create table &Outdata._1 as
(
)
%mend Sales
Now I call the macro
%Sales (Outdata = sales_final_Oct17, dt='2017-10-01');
Libname ABCDEFG
I Create a data set
Data ABCDEFG.all_sales_test;
Set ABCDEFG. all_Sales
sales_final_Oct17_1;
incur_month = month(rept_dt);
run;
Above (1 to 3) is the original code flow and it works fine.
My Problem:
I'm using a dynamic way of generating file name for each month (so that each month I do not manually enter file_name_month and date.
File name code
%let Last_Month = intnx('month', current_date,-1, "beginning");
Name = 'Sales_final';
Last_Month_Name = name|| put(&last_month, monyy7.);
Call SYMPUTX('Last_Month_Name_v', Last_Month_Name);
run;
Call Macro
%Sales(outdata=&Last_Month_Name, dt = 'Dynamic date');
Till this point everything works fine. The moment I create a data set similar to step 3 (above), the code breaks.
Libname ABCDEFG
Data ABCDEFG.all_sales_test;
Set ABCDEFG.all_Sales
Last_Month_Name_1;
incur_month = month(rept_dt);
run;
> Error Message: File ABCDEFG.LAST_MONTH_NAME_1.DATA does not exist.
What should I do to get rid of this error? It seems, if I pass a static name in the macro and then use the same name with "_1" it works fine but when I pass dynamic reference, then the data Set step fails with the above error message.
Any help is much appreciated. I'm new to SAS so excuse me if it's a silly question. Thanks.
In (1) the macro code is using the value of the macro parameter (or local macro variable) OUTDATA to create a dataset. In (2) you are supplying a value for OUTDATA in your call and in (3) you are using the same value again in the set statement.
One way to not have to type the value twice is to store the value into a macro variable then just reference that macro variable's value in steps (2) and (3).
So in (4) you did create a macro variable,Last_Month_Name_v , but you then used the value of a different macro variable, &Last_Month_Name in the macro call. But instead of using the macro variable in the set statement you just referenced some other dataset,Last_Month_Name_1 , that you never mentioned before at all.
Here are the simplified key steps in the process you want for how to create and use the macro variable. I have put in ... to show where I have left out parts of a statement or statements so we can concentrate on the flow of the macro variable and its value.
First you set the macro variable to some name that you want to use. Let's just use anything as the name for this example.
%let last_month_name= anything;
Then you use the value in the macro call to create the dataset. Notice the & before the name, that is what tells the macro processor to replace the name with the value. The period after the name tells the macro processor that is the end of the macro variable name.
%sales(outdata=&last_month_name. .... )
Then you can use the value again later when you want to tell the set statement which dataset to read.
set .... &last_month_name. ;
Now your posted macro %sales does not actually create the dataset named anything. Instead it appears to create a dataset named anything_1. Personally I don't know why that is there, but if you keep it that way then you need add the _1 back to the end of the macro variable's value in the set statement. Just do it in the same way that you did in the macro's code.
set .... &last_month_name._1 ;
Working with macros is one of the harder parts of SAS and can be very confusing to newcomers. Below is a simplified working example that demonstrates the approach I would take.
First we dynamically calculate the name of the table we want to save the results to:
%let current_date = %sysfunc(date());
%let last_month = Sales_final_%sysfunc(intnx(month, &current_date, -1, beginning), monyy7.);
%put &=last_month;
The output from the put statement in the above step is:
LAST_MONTH=Sales_final_OCT2017
Note that in the above code, I'm passing two parameters to %sysfunc(). The first parameter is the call to the intnx() function. The second parameter (monyy7.) is what format to apply to the result being returned from the function call.
Also note that there is no need to concatenate the prefix (Sales_final_) to the %sysfunc() result because when working with the macro language, the result of %sysfunc() is subsituted in place. There is no concatenation operator in the macro language at all - everything is based off of macro substitution.
Then it's a simple case of passing that value into the macro as shown below:
%macro sales(outdata=);
proc sql;
create table &outdata._1 as select * from sashelp.class;
quit;
%mend;
%sales(outdata=&last_month);
You should be able to modify the above into what you need.

Need something like %EVAL for date functions

I'm trying to have a SAS data set automatically limit the results based on date but don't want to manually have to manually change the date through a %Let statement.
If I try
%let BeginDate = %EVAL(MDY(MONTH(TODAY()), 1, YEAR(TODAY()));
I get a "Open code statement recursion detected"... I've tried &SYSFUNC and &SYSEVALF but no luck either. It seems like this should be much simpler... any suggestions would surely be appreciated.
Thanks!
#Joe's method is the most straightforward. Additionally, if you wanted to do this in a datastep with similar syntax you could do:
data _null_;
call symputx('BeginDate_ds',mdy(month(today()),1,year(today())));
run;
%put &BeginDate_ds.;
Depending on what you're doing, you either don't need anything, or you need %SYSFUNC.
If you want to have &begindate evaluate to an actual date value, you would use %SYSFUNC.
However, you have five functions there - that's going to require a bunch of sysfuncs, though I think we can do two not five.
%let begindate = %sysfunc(intnx(MONTH,%sysfunc(today()),0,b));
%put &begindate;
We use INTNX with the MONTH and B(eginning) options to tell SAS to go ahead 0 months (so current month) and to go to the Beginning of that month. A second SYSFUNC grabs TODAY(). You could simplify this more:
%let begindate = %sysfunc(intnx(MONTH,"&sysdate."d,0,b));
%put &begindate;
&SYSDATE is a macro variable that stores the system date when SAS was started up; so only use that if you're okay with that (i.e., if SAS likely/definitely started up today).
With SYSFUNC don't forget that you need to drop quotation marks, with the one big exception of the date constant above - that is okay to use them - but note "MONTH" and "b" are not quoted.

Simplifying a Macro Definition

So I have created a macro, which works perfectly fine. Within the macro, I set where the observation will begin reading, and then how many observations it will read.
But, in my proc print call, I am not able to simply do:
(firstobs=&start obs=&obs)
Because although firstobs will correctly start where I want it, obs does not cooperate, as it must be a higher number than firstobs. For example,
%testmacro(start=5, obs=3)
Does not work, because it is reading in the first 3 observations, but trying to start at observation 5. What I want the macro to do is, start at observation 5, and then read the next 3. So what I did is this:
(firstobs=&start obs=%eval((&obs-1)+&start))
This works perfectly fine when I use it. But I am just wondering if there is a simpler way to do this, rather than having to use the whole %eval... call. Is there one simple call, something like numberofobservations=...?
I don't think there is. You can only simplify your macro a little, within the %eval(). .
%let start=5;
%let obs=3;
data want;
set sashelp.class (firstobs=&start obs=%eval(&obs-1+&start));
run;
Data set options are listed here:
http://support.sas.com/documentation/cdl/en/ledsoptsref/68025/HTML/default/viewer.htm#p0h5nwbig8mobbn1u0dwtdo0c0a0.htm
You could count the obs inside the data step using a counter and only outputting the records desired, but that won't work on something like proc print and isn't efficient for larger data steps.
You could try the point= option, but I'm not familiar with that method, and again I don't think it will work with proc print.
As #Reeza said - there is not a dataset option that will do what you are looking for. You need to calculate the ending observation unfortunately, and %eval() is about as good a way to do it as any.
On a side-note, I would recommend making your macro parameter more flexible. Rather than this:
%testmacro(start=5, obs=3)
Change it to take a single parameter which will be the list of data-set options to apply:
%macro testmacro(iDsOptions);
data want;
set sashelp.class (&iDsOptions);
run;
%mend;
%testmacro(firstobs=3 obs=7);
This provides more flexibility if you need to add in additional options later, which means fewer future code changes, and it's simpler to call the macro. You also defer figuring out the observation counts in this case to the calling program which is a good thing.

SAS - Need to make the filepath used in an INFILE statement dynamic

I have a SAS procedures that runs every morning and it takes in a raw data file for the day called "Filename_20141117.txt". The last part of the file name is changing everyday to the date for that day.
I want to make the INFILE statement in my SAS script smarter so that it knows to get the current day's file. I was trying to do a date concatenation, something like this:
infile cat('G:\Finance\RawData\Filename_',year(today()),month(today()),day(today()),'.txt')
Also, is there a way to declare variables and work with them in SAS, and not have them created as a table in your output? I'm a C/C#/VB programmer learning to program in SAS.
I was thinking that i could make 3 string variables - day, month, and year. Populate each variable with the corresponding value using the Day(), Month() and Year() function, with Today() as the date input. Then i was going to take these 3 variables and concatenate them to get my '20141118' that would be located at the end of the .txt file.
However it doesn't like me doing this. If anyone knows how to do this that would be fantastic. I'm very new to SAS Programming and its concepts.
Thanks in advance.
The most basic version of your statement would be:
INFILE "G:\Finance\RawData\Filename_%sysfunc(today(),yymmddn8.).txt";
To explain fully:
%sysfunc is a macro function in SAS which allows you to use data-step functions in open code, it takes one (optionally two) arguments:
The first argument of %sysfunc is the data-step function in question, in this case we want the value of TODAY(), so that is used.
The second argument (optional) provides the SAS format in which the result should be returned. The SAS format of choice for the date representation you need is yymmddn8. (literally year_year_month_month_day_day, the n represents 'no-separators', and 8 is the length). (e.g. 20141118)
If we were to omit the second argument, we'd get the number of days since 01JAN1960.
If I were to run the statement today it would look like this:
INFILE "G:\Finance\RawData\Filename_20141118.txt";
Also, use of double-quotes here is required because we are using a macro function. If we used single quotes, it would look for a file with the unresolved value of the string above and would most likely fail.
To check what it would resolve to, simply add %PUT to the front of the line, and then just run that line in isolation. The log will show what is returned.

Using SAS macro to import multiple txt files with sequential names

I have 4 txt files that need to be loaded to SAS and save them as 4 sas files. Here are how the text files look like: cle20130805.txt, cle20130812.txt, cle20130819.txt and cle20130826.txt . I used a % Do loop under % Macro in order to get the 4 files imported with only one invoke of the Macro. So Here is my code:
%macro cle;
%do i=20130805 %to 20130826 %by 7;
Data cleaug.cle&i;
infile "home/abc/cle&i..txt" dlm= '|' dsd firstobs=1 obs=100;
input a_no b_no c_no;
run;
%end;
%mend cle;
%cle
I am expect to have 4 sas file saved with only invoke the marco once. However it just can't run successfully. Any ideas where am I doing wrong in the code?
Thanks,
I don't recommend you try to write one macro to import all four files. Either it will be a specific macro you only ever use once - in which case you could just write this by hand and save the time you've already spent - or it will be something you have to modify every single month or whatever that you use it.
Instead, make the macro something that does precisely one file, but includes the information needed to call it easily. In this case, it sounds like you need one parameter: the date, so 20130805 or whatnot. Then give it a reasonable name that really says what it does.
%macro import_files(date=);
Data cleaug.cle&date.;
infile "home/abc/cle&date..txt" dlm= '|' dsd firstobs=1 obs=100;
input a_no b_no c_no;
run;
%mend import_files;
Now you call it:
%import_files(date=20130805)
%import_files(date=20130812)
%import_files(date=20130819)
%import_files(date=20130826)
Just as easy as the macro you wrote above, even hardcoding the four dates. If the dates are predictable in some fashion, you can generate the macro calls very easily as well (if there are more than 4, for example). You could do a directory listing of the location where the files are, or call the macro from a data step using CALL EXECUTE if you really like looping.