Macro only stores part of the string - sas

I create my variable like this:
data Concat_var;
Table_periode_MDR = CAT("\\Afdeling\HS-OKO\Oko\Likviditet\Likviditetsstyring\LCR\Indberetning\2022\",&Periode,"\RLI\Output_Daglig_LCR\MDR_korrektion.csv");
Table_periode_Dagligkorr = CAT("\Afdeling\HS-OKO\Oko\Likviditet\Likviditetsstyring\LCR\Indberetning\2022\",&Periode,"\RLI\Månedlig_LCR_RLI\WORK_QUERY_FOR_DAGLIGEKORREKTIONER.csv");
Table_periode_weights_m = CAT("\Afdeling\HS-OKO\Oko\Likviditet\Likviditetsstyring\LCR\Indberetning\2022\",&Periode,"\RLI\Månedlig_LCR_RLI\Weights_M.csv");
Table_periode_weights_d = CAT("\Afdeling\HS-OKO\Oko\Likviditet\Likviditetsstyring\LCR\Indberetning\2022\",&Periode,"\RLI\Output_Daglig_LCR\Weights_D.csv");
Table_periode_W_D_LCR = CAT("\Afdeling\HS-OKO\Oko\Likviditet\Likviditetsstyring\LCR\Indberetning\2022\",&Periode,"\RLI\Output_Daglig_LCR\W_D_LCR.csv");
Table_periode_W_M_LCR = CAT("\Afdeling\HS-OKO\Oko\Likviditet\Likviditetsstyring\LCR\Indberetning\2022\",&Periode,"\RLI\Månedlig_LCR_RLI\W_M_LCR.csv");
run;
PROC SQL NOPRINT;
SELECT DISTINCT
Table_periode_MDR,
Table_periode_Dagligkorr,
Table_periode_weights_m,
Table_periode_weights_d,
Table_periode_W_D_LCR,
Table_periode_W_M_LCR
INTO :Table_periode_MDR,
:Table_periode_Dagligkorr,
:Table_periode_weights_m,
:Table_periode_weights_d,
:Table_periode_W_D_LCR,
:Table_periode_W_M_LCR
FROM Concat_var;
When looking in the "Concat_var" everything looks right.
The issue is that when I use the string to import files like this:
proc import datafile=&Table_periode_MDR
out=MDR_korrektion
replace
dbms=csv
replace;
getnames=yes;
delimiter=';';
run;
But I get an error because the string is only:
\Afdeling\HS-OKO\Oko\Likviditet\Likviditetsstyring\LCR\Indberetning\2022\202208\RLI\Output_Daglig_LCR\MDR_ko
How can I get the whole string when I create my variable?
I hope you can point me in the right direction.

cat() automatically sets the variable length to 200, and your text is within that range so that does not appear to be the issue.
Although I cannot directly recreate your results, consider creating your macro variables directly. You do not need to use a separate data step with a SQL :into to concatenate them. The macro processor automatically knows where macro variables start and end with & and .
%let folder = \\Afdeling\HS-OKO\Oko\Likviditet\Likviditetsstyring\LCR\Indberetning\2022\;
%let Table_periode_MDR = &folder.\&periode.\RLI\Output_Daglig_LCR\MDR_korrektion.csv;
%let Table_periode_Dagligkorr = &folder.\&periode.\RLI\Månedlig_LCR_RLI\WORK_QUERY_FOR_DAGLIGEKORREKTIONER.csv;
%let Table_periode_weights_m = &folder.\&periode.\RLI\Månedlig_LCR_RLI\Weights_M.csv;
%let Table_periode_weights_d = &folder.\&periode.\RLI\Månedlig_LCR_RLI\Weights_M.csv;
%let Table_periode_W_D_LCR = &folder.\&periode.\RLI\Output_Daglig_LCR\W_D_LCR.csv;
%let Table_periode_W_M_LCR = &folder.\&periode.\RLI\Månedlig_LCR_RLI\W_M_LCR.csv;

Related

Can you include macro in SAS proc import filename?

I need to import the same excel file into SAS every week, and filenames have different dates like below:
file_01012021.xls
file_01072021.xls
When I set up macro variables I'm getting an error due to the " ' " in the MMDDYYYY macro variable
%let MMDDYYY = '01012021'; /*Update each week*/
%let extension '.xls';
%let file = 'File_'
%let filename = &file||put(&MMDDYYYY,8.)||&extension;
proc import
out = dataset1
datafile = "/workspace/&filename"
dbms = xls replace;
run;
Are there ways to get this to work?
You do not need quotes in a macro variable assignment statement, and you do not need to concatenate them with a data step concatenation statement. Simply put them together by resolving each macro variable.
%let mmddyy = 01012021;
%let extension = .xls;
%let file = File_;
%let filename = &file.&mmddyy.&extension;

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;

Reading n files into SAS to create n datasets

I have just started learning SAS, and I'm using the following code to read xlsx files:
proc import out = data_lib.dataset_1
datafile = 'C:\data_folder\data_file_1.xlsx'
dbms = xlsx replace;
sheet = 'Sheet1';
getnames = yes;
run;
This has been working fine for me, but I'd like to supply the code with a list of filenames to read and a list of dataset names to create, so that the code need only appear once. I have looked at several instructional web pages about using macros, but I've been unable to translate that information into working code. Any help would be greatly appreciated. I'm using SAS 9.4, 64 bit.
I'd offer a modified version of kl78's suggestion, avoiding macros. Again, assuming you have the file names in a SAS data set, use a data step to read the list of file names and use call execute to run your proc import code for each file name.
data _null_;
set t_list;
call execute (
"proc import out = " || datasetname || "
datafile = '"|| filename ||"'
dbms = xlsx replace;
sheet = 'Sheet1';
getnames = yes;
run;");
run;
So, suppose you have your filenames and datanames in a table called t_list with variablename datasetname and filename, you could try something like this:
%macro readexcels;
data _null_;
set t_list (nobs=nobs);
call symputx(cat("libname_",_n_), datasetname);
call symputx(cat("filename_",_n_), filename);
if _n_=1 then
call symputx("nobs", nobs);
run;
%do i=1 %to &nobs;
proc import out = &&libname_&i;
datafile = "&&filename_&i"
dbms = xlsx replace;
sheet = 'Sheet1';
getnames = yes;
run;
%end;
%mend;
%readexcels;
In the datastep you read every entry of your table with datasetname and listname and create macrovariables with a numeric suffix. You only need to create a macrovariable for the number of entries once, so i did it when n = 1, you could also do this at eof.
Then you have a do loop, and with every loop you read the specific excel and write it in the specific dataset.
You need to write it like &&libname&i, because at first this resolves to &libname_1, and after this resolves to the variablevalue...

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.

SAS creating and populating dataset variables from macro variables

I have a group of data sets where certain variables have been defined as having lengths >2000 characters. What I want to do is create a macro that identifies these variables and then creates a set of new variables to hold the values.
doing this in base code would be something like:
data new_dset;
set old_dset:
length colnam1 colnam2 colnam3 2000.;
colnam1 = substr(long_column,1,2000);
colnam2 = substr(long_column,2001,2000);
run;
I can build up the list of variable names and lengths as a set of macro variables, But I don't know how to create the new variables from the macro variables.
What I was thinking it would look like is:
%macro split;
data new_dset;
set old_dset;
%do i = 1%to &num_cols;
if &&collen&i > 2000 then do;
&&colnam&i 1 = substr(&&colnam&i,1,2000);
end;
%en;
run;
%mend;
I know that doesn't work, but that's the idea I have.
If anyone can help em work out how I can do this I would be very grateful.
Thanks
Bryan
Your macro doesn't need to be an entire data step. In this case it's helpful to see exactly what you're replicating and then write a macro based on that.
So your code is:
data new_dset;
set old_dset:
length colnam1 colnam2 colnam3 2000.;
colnam1 = substr(long_column,1,2000);
colnam2 = substr(long_column,2001,2000);
run;
Your macro then really needs to be:
length colnam1 colnam2 colnam3 2000.;
colnam1 = substr(long_column,1,2000);
colnam2 = substr(long_column,2001,2000);
So what you can do is put that in a macro:
%macro split(colname=);
length &colname._1 &colname._2 $2000;
&colname._1 = substr(&colname.,1,2000);
&colname._2 = substr(&colname.,2001,4000);
%mend;
Then you generate a list of calls:
proc sql;
select cats('%split(colname=',name,')') into :calllist separated by ' '
from dictionary.columns
where libname = 'WORK' and memname='MYDATASET'
and length > 2000;
quit;
Then you run them:
data new_dset;
set old_dset;
&calllist;
run;
Now you're done :) &calllist contains a list of %split(colname) calls. If you may need more than 2 variables (ie, > 4000 length), you may want to add a new parameter 'length'; or if you're in 9.2 or newer you can just use SUBPAD instead of SUBSTR and generate all three variables for each outer variable.