SAS - Macro Variable in Proc Export file name - sas

I'm trying to transfer code that pulls a survey sample every month into a cronjob, but the last step I'm having an issue with in automating the code is with the file name in the proc export step.
I have the following macro variables defined at the beginning of the code:
%let today = date();
%let month = month(today);
%let year = year(today);
After I pull the data from our database and filter appropriately, I have a code that outputs the files as a pipe delimited .txt file. This file format is important to preserve:
proc export data=mkt.project_&timestamp._group
outfile="/filepath/project_&year.&month._group" dbms=dlm Replace;
delimiter='|';
run;
The file output name doesn't recognize the macro variables, however, so instead of getting the year and month, it just names them as "project_&year.&month._group".
Can anybody help with this?
Thanks!

Macro variables contain text.
You have set yours to text strings that look like SAS function calls. But then you did not use the strings to generate SAS code where such a function call would make sense. Instead you put the function call into the name of a file.
440 %let today = date();
441 %let month = month(today);
442 %let year = year(today);
443 %put "/filepath/project_&year.&month._group";
"/filepath/project_year(today)month(today)_group"
One way to execute SAS functions in macro code is to use the macro function %sysfunc(). If you want to generate the 6 digit string in they style YYYYMM you can use the YYMMN6. format. So you could generate your filename like this:
"/filepath/project_%sysfunc(date(),yymmn6.)_group"
Or your other macro variables like this:
%let today = %sysfunc(date());
%let month = %sysfunc(month(&today),z2.);
%let year = %sysfunc(year(&today));

Related

SAS: How to proc import with different date?

I have 10 csv files that would like to do PROC IMPORT, one at each time, depending on the value user input. For example, user will input 201801 to run January 2018 csv file(PROC IMPORT).
For that case, i tried to use substring &period(which stores the yymmn6. value which user input) and then from there, put in proc import. My code as below:
%let period=201812;
%macro convertdate();
data convertdate;
year=substr("&period",1,4);
month=substr("&period",5,2);
if month='01' then newmonth='jan';
if month='02' then newmonth='feb';
if month='03' then newmonth='mac';
if month='04' then newmonth='apr';
if month='05' then newmonth='may';
if month='06' then newmonth='jun';
if month='07' then newmonth='jul';
if month='08' then newmonth='aug';
if month='09' then newmonth='sep';
if month='10' then newmonth='oct';
if month='11' then newmonth='nov';
if month='12' then newmonth='dec';
run;
/*Assign convertdate the only distinct record into macro, then set as the data step for proc import*/
%if month='01' %then %do;
%let newmonth=jan;
%end;
/*proc import and remaining transformation from existing script*/
proc import out=rmr_raw_source
file="/sasdata/source/user_files/re_&newmonth.2018.xlsx"
dbms=xlsx replace;
sheet="re_&newmonth.2018";
getnames=no;
dbsaslabel=none;
run;
%mend;
%convertdate;
However, it wont work. I am getting warning below:
WARNING: Apparent symbolic reference NEWMONTH not resolved.
Does anyone have better solution to it?
You can try something as follows:
%let period = '201801'; /*7*/
%global month;
data test123;
period = . /*1*/
full_date = cats(period,'01'); /*2*/
format converted_date DATE10.; /*3*/
converted_date = input(full_date, yymmdd8.); /*4*/
month = put(converted_date,monname3.); /*5*/
call symput('month',month); /*6*/
run;
Create a SAS variable out of Macro.
Set to first date of the given month.
Create a new var and set the date format.
convert the TEXT date to SAS date.
get the month name out of date created above.
create a macro variable named 'month' out of SAS variable month.
Make macro variable 'month' global.
You can now use month macro variable anywhere in the code.

set a dataset by dereferencing a variable

I would like to set a dataset by using a reference to dataset name however Iam getting error message: ERROR: File dataset_name123 does not exist(work.dataset123 does exist) What is wrong?
data _null_;
%let product = 'dataset_name123';
set work.&product nobs = row_no;
put row_no;
put &product;
run;
Member names are not quoted. Remove the quotes from your macro variable. In macro code everything is character so there is no need to add quotes around string literals. The quotes become part of the value of the macro variable.
%let product = dataset_name123;
%put &=product;
data _null_;
set work.&product nobs = row_no;
put row_no;
put "&product";
stop;
run;
If you do include quotes in a dataset reference then SAS will interpret it as the physical name of the dataset file itself. So code like:
data want;
set 'dataset_name123';
run;
would look for a filename 'dataset_name123.sas7bdat' in the current working directory.
It is not a great idea to do a %let statement in a data step. Macrovariables and SAS variables are created differently.
There are two problems in this code. First one is quotes around macrovariable, which after resolution will be used for table name and hence your query fails as table names cannot be in quotes .
second one is put statement for macro variable for macro variable to resolve you need %put.
below is modified code.
data class;
set sashelp.class;
run;
data _null_;
%let product = class;
set work.&product nobs = row_no;
put row_no;
%put &product;
run;

Creating a filename with today's date in SAS.

I would like to be able to save a file in SAS with today's date. I'm having trouble creating the file path with today's date.
Given that today's current date is 3/27/2018. I would like the created file path to be this:
"/sasFolder/MyFileName(3-27-2018).xlsx"
My current code is:
data _null_;
call symput('dateMonth', month(date()));
call symput('dateDay', day(date()));
call symput('dateYear', year(date()));
run;
%let filePath = "/sasFolder/MyFileName(&dateMonth.-&dateDay.-&dateYear.).xlsx";
data _null_;
put &filePath;
run;
Currently my output is this, with _ representing spaces.
"/sasFolder/MyFileName(___________3-__________26-________2018).xlsx"
I would like for the filename to not have all theses extra spaces in the name. Any ideas on how to do this?
You can do this very easily without a data step using %sysfunc() - this lets you call a SAS function and apply a format at the same time, eg:
%let filePath = "/sasFolder/MyFileName(%sysfunc(today(), mmddyyd10.)).xlsx";
%put &=filepath;
Which gives:
FILEPATH="/sasFolder/MyFileName(03-27-2018).xlsx"
There is a format for that date style already. Looks like you using the MMDDYYD10. format. You could use it in your data step code.
data _null_;
call symputx('datestamp', put(date(),mmddyyd10.));
run;
%let filePath="/sasFolder/MyFileName(&datestamp).xlsx";
Or skip the data step and use the %sysfunc() macro function to call the date() function and apply the format.
%let datestamp=%sysfunc(date(),mmddyyd10.);
You can even eliminate the extra macro variable.
%let filePath="/sasFolder/MyFileName(%sysfunc(date(),mmddyyd10.)).xlsx";
Note that I recommend that you switch to using YYMMDD format instead of MMDDYY format. First it will prevent your English friends from confusing December tenth and October twelfth. Second it will allow your file names to sort in chronological order.
Figured it out. Needed to change symput to symputx to remove the spaces.
The code is:
data _null_;
call symputx('dateMonth', month(date()));
call symputx('dateDay', day(date()));
call symputx('dateYear', year(date()));
run;
%let filePath = "/sasFolder/MyFileName(&dateMonth.-&dateDay.-&dateYear.).xlsx";
data _null_;
put &filePath;
run;

Using %PUT to correctly format the dynamic file name

I have a SAS script that reads in a CSV file and stores it in a SAS data set:
LIBNAME IN '\\path\Data';
FILENAME CSV '\\path\Data\DT.csv';
DATA IN.DT;
INFILE CSV DLM=',' DSD FIRSTOBS=1;
INPUT KEY VALUE1 VALUE2;
RUN;
I want to change it such that instead of expecting the input to be named DT.csv, it would accept an input named DT-2016-03-03-TEST.csv, or whatever the current date is. In other words, I need to use a dynamic value in my FILENAME statement.
Here is what I have so far:
%LET curday = %SYSFUNC(day("&sysdate"d));
%LET curmonth = %SYSFUNC(month("&sysdate"d));
%LET curyear = %SYSFUNC(year("&sysdate"d));
%PUT %SYSFUNC(PUTN(&curday, z2.));
FILENAME CSV "\\path\Data\DT-&curyear-&curmonth-&curday-TEST.csv";
But the string it generates is like Data\DT-2016-3-3-TEST.csv rather than Data\DT-2016-03-03-TEST.csv
In other words, the trailing zeros are not there. What am I doing incorrectly?
You'll need to use either a macro variable or a big group of macro functions (whichever you'd like). We'll go with creating macro variables for readability purposes. Based upon what you've said, we know a few things about the pattern:
It starts with DT-
It has today's date in a yyyy-mm-dd format
It ends in .csv
Two of these are static values, and one needs to be dynamic in a specific format. Let's get crackin'.
Start off by storing the path in its own macro variable. This makes the code more generalizable to other applications (i.e. you can copy/paste old code for new programs! It's good to be lazy in the programming world).
%let path = \\path\data;
Next, let's build our dynamic pattern using a %let statement. We know it starts with DT-:
___________________________________________
%let file = DT-
___________________________________________
We can now cross #1 off the list! Let's knock out #2.
Two functions will help us get this in the order that we want:
%sysfunc()
today()
We'll encapsulate today() with %sysfunc(). %sysfunc() lets us run most non-macro-language SAS functions, and also has the added benefit of returning the value in a format that you desire using an additional argument. This is really helpful for us here.
So, let's grab today's date as a numeric SAS date, then convert it to yymmddx10 format, where x is some delimiter keyword. We'll use yymmddd10. - that is, a format that specifies yyyy-mm-dd. The extra d means dash.
___________________________________________
%let file = DT-%sysfunc(today(), yymmddd10.)
___________________________________________
2 is now out of the way. Hard part's over! All we need to do is append .csv to it, and we'll be all set.
___________________________________________
%let file = DT-%sysfunc(today(), yymmddd10.).csv;
___________________________________________
You can confirm the macro variable file's value with a %put statement:
%put NOTE: This is my filename: &file;
You should see in green text in the log NOTE: This is my filename: DT-2016-03-03.csv
Now, we'll just put it all together:
%let path = \\path\data;
%let file = DT-%sysfunc(today(), yymmddd10.).csv;
libname IN "&path";
filename CSV "&path\&file";
data in.DT;
infile csv dlm=',' dsd firstobs=1;
input key value1 value2;
run;
You've now got a dynamic way to read in these CSVs, and you can adapt this code elsewhere. Awesomesauce. I think you've earned yourself a celebratory coffee, and maybe a biscotti or two; don't go too crazy.
Stu's answer is absolutely correct. For the tl;dr version.
%put echos stuff to the log. All you are doing is "putting" the result of %SYSFUNC(PUTN(&curday, z2.)) to the log. You are not updating the value in &curday.
Try
%LET curday = %SYSFUNC(PUTN(&curday, z2.));
Do that for the other curmonth, too.
Take the time and read Stu's answer.

Text manipulation of macro list variables to stack datasets with automated names

I have written a macro that accepts a list of variables, runs a proc mixed model using each variable as a predictor, and then exports the results to a dataset with the variable name appended to it. I am trying to figure out how to stack the results from all of the variables in a single data set.
Here is the macro:
%macro cogTraj(cog,varlist);
%let j = 1;
%let var = %scan(&varlist, %eval(&j));
%let solution = sol;
%let outsol = &solution.&var.;
%do %while (&var ne );
proc mixed data = datuse;
model &cog = &var &var*year /solution cl;
random int year/subject = id;
ods output SolutionF = &outsol;
run;
%let j = %eval(&j + 1);
%let var = %scan(&varlist, %eval(&j));
%let outsol = &solution.&var.;
%end;
%mend;
/* Example */
%cogTraj(mmmscore, varlist = bio1 bio2 bio3);
The result would be the creation of Solbio1, Solbio2, and Solbio3.
I have created a macro variable containing the "varlist" (Ideally, I'd like to input a macro variable list as the argument but I haven't figured out how to deal with the scoping):
%let biolist = bio1 bio2 bio3;
I want to stack Solbio1, Solbio2, and Solbio3 by using text manipulation to add "Sol" to the beginning of each variable. I tried the following, outside of any data step or macro:
%let biolistsol = %add_string( &biolist, Sol, location = prefix);
without success.
Ultimately, I want to do something like this;
data Solbio_stack;
set %biolistsol;
run;
with the result being a single dataset in which Solbio1, Solbio2, and Solbio3 are stacked, but I'm sure I don't have the right syntax.
Can anyone help me with the text string/dataset stacking issue? I would be extra happy if I could figure out how to change the macro to accept %biolist as the argument, rather than writing out the list variables as an argument for the macro.
I would approach this differently. A good approach for the problem is to drive it with a dataset; that's what SAS is good at, really, and it's very easy.
First, construct a dataset that has a row for each variable you're running this on, and a variable name that contains the variable name (one per row). You might be able to construct this using PROC CONTENTS or sashelp.vtable or dictionary.tables, if you're using a set of variables from one particular dataset. It can also come from a spreadsheet you import, or a text file, or anything else really - or just written as datalines, as below.
So your example would have this dataset:
data vars_run;
input name $ cog $;
datalines;
bio1 mmmscore
bio2 mmmscore
bio3 mmmscore
;;;;
run;
If your 'cog' is fairly consistent you don't need to put it in the data, if it is something that might change you might also have a variable for it in the data. I do in the above example include it.
Then, you write the macro so it does one pass on the PROC MIXED - ie, the inner part of the %do loop.
%macro cogTraj(cog=,var=, sol=sol);
proc mixed data = datuse;
model &cog = &var &var*year /solution cl;
random int year/subject = id;
ods output SolutionF = &sol.&var.;
run;
%mend cogTraj;
I put the default for &sol in there. Now, you generate one call to the macro from each row in your dataset. You also generate a list of the sol sets.
proc sql;
select cats('%cogTraj(cog=',cog,',var=',name,',sol=sol)')
into :callList
sepearated by ' '
from have;
select cats('sol',name') into :solList separated by ' '
from have;
quit;
Next, you run the macro:
&callList.
And then you can do this:
data sol_all;
set &solList.;
run;
All done, and a lot less macro variable parsing which is messy and annoying.