Any help please?
When displaying the variable vMonth, it is working but when concatenating it with the library name, the following issue is obtained.
Program:
%LET lastdaypreviousmonth = put(intnx('month', today(), -1, 'E'),mmyyn4.);
%LET vMonth = cats('RM',&lastdaypreviousmonth);
PROC SQL;
SELECT &vMonth,*
FROM MASU.&vMonth
WHERE nsgr = '040';
QUIT;
Log file :
27 %LET lastdaypreviousmonth = put(intnx('month', today(), -1, 'E'),mmyyn4.);
28 %LET vMonth = cats('RM',&lastdaypreviousmonth);
29
30 PROC SQL;
31
32 SELECT &vMonth,*
33 FROM MASU.&vMonth
34 WHERE nsgr = '040';
NOTE: PROC SQL set option NOEXEC and will continue to check the syntax of statements.
NOTE: Line generated by the macro variable "VMONTH".
34 MASU.cats('RM',put(intnx('month', today(), -1, 'E'),mmyyn4.))
_ _
79 79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
The macro code is just doing what you told it to do. Add some %PUT statements to see what values you have put into your macro variables. The macro processer will not treat strings like put or cats any differently than it would treat the string xyz or 123.
If you want to call SAS functions in macro code you need to wrap each call with the %sysfunc() macro function. Not all functions can be called this way. In particular instead of the type flexible PUT() and INPUT() functions use the type specific versions instead. But in this case you can just use the format parameter of the %SYSFUNC() call instead of the function call. Do not include quotes in your string literals, everything is a string literal to the macro processor.
Use this:
%LET lastdaypreviousmonth=%sysfunc(intnx(month,%sysfunc(today()),-1, E),mmyyn4.);
There is no need to ever use the CAT...() functions in macro code. To concatenate macro variable value just expand them where you want them to appear.
%LET vMonth = RM&lastdaypreviousmonth.;
Related
Dummy data:
MEMNAME _var1 var2 var3 var4
XY XYSTART_1 XYSTATT_2 XYSTAET_3 XYSTAWT_4
I want to create a macro variable that will have data as TEST_XYSTART, TEST_XYSTATT, TEST_XYSTAET, TEST_TAWT.... how can I do this in datastep without using call symput because I want to use this macro variable in the same datastep (call symput will not create macro variable until I end the datastep).
I tried as below (not working), please tell me what is the correct way of write the step.
case = "TEST_"|| strip(reverse(substr(strip(reverse(testcase(i))),3)));
%let var = case; (with/without quotes not getting the desired result).
abc= strip(reverse(substr(strip(reverse(testcase(i))),3)));
%let test = TEST_;
%let var = &test.abc;
I am getting correct data with this statement: strip(reverse(substr(strip(reverse(testcase(i))),3)))
just not able to concatenate this value with TEST_ and assign it to the macro variable in a datastep.
Appreciate your help!
It makes no sense to locate a %LET statement in the middle of a data step. The macro processor evaluates the macro statements first and then passes the resulting code onto SAS to evaluate. So if you had this code:
data want;
set have;
%let var=abc ;
run;
It is the same as if you placed the %LET statements before the DATA statement.
%let var=abc ;
data want;
set have;
run;
If you want to reference a variable dynamically in a data step then use either an index into an array.
data want;
set have;
array test test_1 - test_3;
new_var = test[ testnum ] ;
run;
Or use the VvalueX() function which will return the value of a variable whose name is a character expression.
data want;
set have;
new_var = vvaluex( cats('test_',testnum) );
run;
I need to output lots of different datasets to different text files. The datasets share some common variables that need to be output but also have quite a lot of different ones. I have loaded these different ones into a macro variable separated by blanks so that I can macroize this.
So I created a macro which loops over the datasets and outputs each into a different text file.
For this purpose, I used a put statement inside a data step. The PUT statement looks like this:
PUT (all the common variables shared by all the datasets), (macro variable containing all the dataset-specific variables);
E.g.:
%MACRO OUTPUT();
%DO N=1 %TO &TABLES_COUNT;
DATA _NULL_;
SET &&TABLE&N;
FILE 'PATH/&&TABLE&N..txt';
PUT a b c d "&vars";
RUN;
%END;
%MEND OUTPUT;
Where &vars is the macro variable containing all the variables needed for outputting for a dataset in the current loop.
Which gets resolved, for example, to:
PUT a b c d special1 special2 special5 ... special329;
Now the problem is, the quoted string can only be 262 characters long. And some of my datasets I am trying to output have so many variables to be output that this macro variable which is a quoted string and holds all those variables will be much longer than that. Is there any other way how I can do this?
Do not include quotes around the list of variable names.
put a b c d &vars ;
There should not be any limit to the number of variables you can output, but if the length of the output line gets too long SAS will wrap to a new line. The default line length is currently 32,767 (but older versions of SAS use 256 as the default line length). You can actually set that much higher if you want. So you could use 1,000,000 for example. The upper limit probably depends on your operating system.
FILE "PATH/&&TABLE&N..txt" lrecl=1000000 ;
If you just want to make sure that the common variables appear at the front (that is you are not excluding any of the variables) then perhaps you don't need the list of variables for each table at all.
DATA _NULL_;
retain a b c d ;
SET &&TABLE&N;
FILE "&PATH/&&TABLE&N..txt" lrecl=1000000;
put (_all_) (+0) ;
RUN;
I would tackle this but having 1 put statement per variable. Use the # modifier so that you don't get a new line.
For example:
data test;
a=1;
b=2;
c=3;
output;
output;
run;
data _null_;
set test;
put a #;
put b #;
put c #;
put;
run;
Outputs this to the log:
800 data _null_;
801 set test;
802 put a #;
803 put b #;
804 put c #;
805 put;
806 run;
1 2 3
1 2 3
NOTE: There were 2 observations read from the data set WORK.TEST.
NOTE: DATA statement used (Total process time):
real time 0.07 seconds
cpu time 0.03 seconds
So modify your macro to loop through the two sets of values using this syntax.
Not sure why you're talking about quoted strings: you would not quote the &vars argument.
put a b c d &vars;
not
put a b c d "&vars";
There's a limit there, but it's much higher (64k).
That said, I would do this in a data driven fashion with CALL EXECUTE. This is pretty simple and does it all in one step, assuming you can easily determine which datasets to output from the dictionary tables in a WHERE statement. This has a limitation of 32kiB total, though if you're actually going to go over that you can work around it very easily (you can separate out various bits into multiple calls, and even structure the call so that if the callstr hits 32000 long you issue a call execute with it and then continue).
This avoids having to manage a bunch of large macro variables (your &VAR will really be &&VAR&N and will be many large macro variables).
data test;
length vars callstr $32767;
do _n_ = 1 by 1 until (last.memname);
set sashelp.vcolumn;
where memname in ('CLASS','CARS');
by libname memname;
vars = catx(' ',vars,name);
end;
callstr = catx(' ',
'data _null_;',
'set',cats(libname,'.',memname),';',
'file',cats('"c:\temp\',memname,'.txt"'),';',
'put',vars,';',
'run;');
call execute(callstr);
run;
Is there a way to override the default behavior of character length being set by the first value encountered and instead set all character data for a session to have the same fixed length?
Much of the data I work with daily is of a similar format/structure, such as a .csv or .txt. I find that using an infile statement with list input works well for importing this kind of data.
For instance, suppose I have a text file myData.txt.
myData.txt
string1 string2 num1 string3 num2
hello there 12 this 33
is some 45 sample 2
data for 8 you 12
I would then use code like this to bring it in.
%let dataDirectory = C:\path\to\file;
%let dataFile = myData.txt;
filename myFile "&dataDirectory.\&dataFile.";
data in_data;
infile myFile dsd dlm = '09'x firstobs = 2;
length
string1 $ 50.
string2 $ 50.
num1 8
string3 $ 50.
num2 8
;
input
string1 $
string2 $
num1
string3 $
num2
;
run;
filename myFile clear;
I find that it is important to have the length statement so that none of my data is truncated. Since the data sets are not particularly large, it makes sense to set all the character lengths to some fixed amount which will guarantee no truncation occurs. I find that the default numeric length is sufficient.
The problem with this approach is that any time a variable name needs to be changed etc, I need to make an alteration in both the length and input statements. This gets to be a nuisance, especially when there are 150 variables, and I'm hoping it is unnecessary.
List input seems appropriate to my needs. I could use column input, but then I'd have to fiddle around with defining column widths. I can't think of a way to make that a simple process when handling 150 columns. Being able to globally define all character lengths, as with the default 8 for numeric, would solve my problem. Is this possible? Or, maybe you have a better method for bringing in such data as myData.txt?
You could use a macro variable to store your default length. Then you can change it in one place.
You can use a variable list in your INPUT statement so that you don't need to worry about typing variable names more than once.
%let dataDirectory = C:\path\to\file;
%let dataFile = myData.txt;
%let defLength = $80 ;
data in_data;
infile "&dataDirectory/&dataFile" dsd dlm='09'x firstobs=2 truncover ;
length
string1 &defLength
string2 &defLength
num1 8
string3 &defLength
num2 8
;
input (_all_) (:) ;
run;
You can specify how many rows SAS should use to determine field attributes with the "guessingrows" option using proc import. That way proc import will take care of any number of new variables you may have.
proc import out=importeddata
datafile= "/examplepath/file.txt"
dbms=dlm replace;
delimiter='09'X;
getnames=YES;
guessingrows=5000;
run;
If you keep your length statement in the proper order you can use a SAS variable list for the INPUT statement. You don't need the $sign in the input statement. If you have INFORMATS for some variable use an INFORMAT statement to associate.
data in_data;
infile myFile dsd dlm = '09'x firstobs = 2;
length
string1 $ 50.
string2 $ 50.
num1 8
string3 $ 50.
num2 8
;
input (string1--num2)(:);
run;
I have a program that I want to run on several years. Therefore at some time I have to select my data
data want;
set have(where='2014');
run;
To try on several years, I have a macro variable, that I define as
%let an=14
/*It is 14 and not 2014, because elsewhere I need it that way.*/
But then when I try to put it in my program it does not work at all
data want;
set have(where="&&20&an.");
run;
I would appreciate some help
First edit : changed ' ' into " ", but still does not work
Second edit and answer
"20&an"
The answer you arrived at (20&an) is correct - you are all set. You don't even need to read the rest of this answer I've posted :-)
However I noticed you were a bit confused about & vs. &&. If you'd like to know more about that, I put together some extra info on the difference between & and &&, as well as the purpose of &&, in SAS macro evaluation.
The & is the most common symbol - you simply use that to evaluate/de-reference a variable. So:
%LET an = 14 ;
%PUT ----- an ;
%PUT ----- &an ;
Output:
----- an
----- 14
So as you can see, you must put a & before the variable name in order to de-reference it to its value. Omitting the & simply prints the string an, which happens to be the name of the variable in this case. For most macro coding, & is all you will ever need. & in SAS macro is like $ in shell, * in C, etc.
Now, what is && for? It exists to enable you to have dynamic macro variable names. That is, having a macro variable whose value is the name of another macro variable. If you are familiar with C, you can think of this as a pointer to a pointer.
The way SAS evaluates && is in two passes. In the first pass, it converts && to &. At the same time, any & symbols it sees in that pass will be used to de-reference the variable names they are next to. The idea is for these latter expressions to resolve to a variable name. Then, in the second pass, the remaining & symbols (all originally && symbols) de-reference whatever variable names they now find themselves next to.
Here is an example with sample output:
%LET x = 3;
%LET name_of_variable = x;
%PUT ----- &x;
%PUT ----- &&name_of_variable;
%PUT ----- &&&name_of_variable;
Output:
----- 3
----- x
----- 3
In the first %PUT, we are just using plain old &, and thus we are doing what we did before, reading and printing the value that x holds. In the second %PUT, things get slightly more interesting. Because of the &&, SAS does two passes of evaluation. The first one converts this:
%PUT ----- &&name_of_variable;
To this
%PUT ----- &name_of_variable;
In the second pass, SAS does the standard & evaluation to print the value held in name_of_variable - the string x, which happens to be the name of the other variable we are using. Of course, this example is especially contrived: why would you write &&name_of_variable when you could have just written &name_of_variable?
In the third %PUT, where we now have the &&&, SAS does two passes. Here is where we finally see the true purpose of &&. I will put pieces of the expression in parentheses so you can see how they are evaluated. We go from this:
%PUT ----- (&&)(&name_of_variable);
To this:
%PUT ----- &x;
So the in the first pass, the && was converted to &, and &name_of_variable was a simple de-referencing of name_of_variable, evaluating to the content it is holding, which as we said was x.
So in the second pass, we are just left with the simple evaluation:
%PUT ----- &x;
As we had set x equal to 3, this evaluates to 3.
So in a sense, saying &&&name_of_variable is saying "Show me the value of the variable whose name is stored in name_of_variable."
Here is a motivating example for why you would want to do this. Suppose you had a simple macro subroutine that added an arbitrary number to a numerical value stored in a SAS macro variable. To do that, the subroutine would have to know the amount to add, but more importantly, it would need to know the name of the variable to add to. You would accomplish this dynamic variable naming via the && mechanism, like so:
%MACRO increment_by_amount (var_name = , amount = );
%LET &var_name = %EVAL (&&&var_name + &amount) ;
/* Note: this could also begin with %LET &&var_name = .... */
%MEND;
Here we are saying: "Let the variable whose name is held in var_name (i.e. &var_name) equal the value of the variable whose name is held in var_name (i.e. &&&var_name) plus the value held in amount (i.e. &amount).
When you call a subroutine like this, make sure you are passing the variable name, not the value. That is, say this:
%increment_by_amount (var_name = x , amount = 3 );
Not this:
%increment_by_amount (var_name = &x , amount = 3);
So an example of invocation would be:
%LET x = 3;
%PUT ----- &x;
%increment_by_amount (var_name = x , amount = 3 );
%PUT ----- &x;
Output:
----- 3
----- 6
Hi I am building a dataset, but the data I am merging is in different formats.
From the Excel sheet i import its in numeric 8, and the other 2 datasets im merging to are character 20, so I want to change the numeric 8 to char 20.
How can I change the variable acctnum, to char 20? (I also want to keep this as its name, as I presume a new variable will be created)
data WORK.T82APR;
set WORK.T82APR;
rename F1 = acctnum f2 = tariff;
run;
proc contents data=T82APR;
run;
While this thread is already dead, I thought I'd way in and answer why the 14 digits conversion became in E notation.
Typically, or rather, unless otherwise specified, numeric formats in SAS use BEST12 format. As such, when a numeric value is longer than 12 characters (including any commas and periods), BEST12 chooses E notation as the best way to format the value.
The input function, in that case receives the formatted value put(acctnum, BEST12.). There would've been 2 ways around it.
Either use
input(put(acctnum, 14.), $20.);
Or, change the format of the variable using the format statement (directly in a data step or with proc datasets like) - this has the added benefit that if you open the table in SAS, you will see the 14 digits and not the scientific formatted value.
proc datasets library=work nolist;
modify dsname;
format acctnum 14.;
run;
Vincent
Try this:
data WORK.T82APR ;
set WORK.T82APR;
acctnum = put(F1, $20.);
rename f2 = tariff;
run;
Ok, I didn't pay attention to your own rename statement, so I adjusted my answer to reflect that now.