I am looking for a way to create a string variable containing certain values of the dataset while going through the data step.
Example data set work.test:
AddToStringYN Value
Y One
Y Two
N Three
Y Four
N Five
So in the end, the variable would look like: OneTwoFour (or even better FourTwoOne).
This looks so simple, but I can't seem to find a way to do it.
I also tried to work with macro variables like this:
%let stringvar=;
Data _null_;
set work.test;
if AddToStringYN = "Y" then do;
call symput('stringvar',"&stringvar" || strip(value));
end;
Run;
But this gives:
GLOBAL STRINGVAR Four
So I only get the last value. I get that this must be because of some misunderstanding of mine about this macro facility, but I don't understand why there is only the last value in the variable.
I thought it was only the last time the symput was called that it was actually executed or something, but then when I adjust the code to:
%let stringvar=;
Data _null_;
set work.test;
if AddToStringYN = "Y" then do;
call symput('stringvar'||strip(value),"&stringvar" || strip(value));
end;
Run;
Then I do get them all:
GLOBAL STRINGVARONE One
GLOBAL STRINGVARTWO Two
GLOBAL STRINGVARFOUR Four
So my last guess is that going through the data step, the 'call symput...' line is actually added to the macro processor where the "&stringvar" is already replaced and only after the final statement are they all executed.
Is this a good assumption or is there another explanation?
And back to the original question: is there an easy way to achieve this (having the desired variable)?
The following is my answer to your identical question on RunSubmit.com. I think you and #Fabio may be over-engineering the solution, it doesn't need any iterating data step code at all...
First, the easy way to do what you're trying to do is like this:
proc sql;
select Value into :StringVar separated by ''
from work.test
where AddToStringYN='Y'
;
quit;
Here, you can take advantage of the SQL interface with SAS/MACRO, using the select into syntax. You could even add an order by clause to get a particular order you're looking for.
Second, since you've happened upon something about the way SAS macro works and you're keen to understand it: in your first example, the first thing the compiler does before executing your code is to resolve the value of &stringvar, which at that point is empty. So after compilation, with this token replaced, your code looks like this to SAS...
%let stringvar=;
Data _null_;
set work.test;
if AddToStringYN = "Y" then do;
call symput('stringvar',"" || strip(value));
end;
Run;
...then SAS goes ahead and runs that code (which happens to be valid code, but is concatenating an empty string to the start of something). And because of the way the data step works, each iteration of the data step is in fact replacing the value of StringVar, which is why at the end of the data step, it's left with the last value that was read in.
Greetings
Seems simple enough, here is my solution:
data a;
set test end=eof;
length cat $100.;
retain cat;
if AddToStringYN = "Y" then do;
cat=trim(left(cat))||trim(left(value));
end;
if eof then do;
call symput("VAR",cat);
output;
end;
run;
%put VAR=&VAR;
in this example you have the concatenation of your variable in the A dataset in the column "CAT" and you have a macrovariable VAR with the same list
Related
Just for the debugging purpose: Is there a way to stop executing a series of submitted statements on the first error?
Let's say, I have three steps of code where the second statement has an error. Assume that I run all of them at once in the SAS window. Then, I expect SAS to successfully execute the first sentence and to stop working due to the error detected in the second sentence. Then I can easily go there to fix this.
But what's actually happening is that SAS tries to execute all three steps (i.e., 1st, 2nd (with error, though), and 3th). Hope that there is a solution for this.
Two remarks:
I found that the below code may help, but it didn't actually. Otherwise, please enlighten me.
options syntaxcheck dmssynchk;
I don't want to use the SAS option, errorabend because it shuts down SAS session itself. I just want my SAS stop "the submitted code" and want to fix the issue.
Thanks in advance.
There is a commonly used macro called %runquit that allows that... Not sure where it originates from, but here it goes:
%macro runquit;
; run; quit;
%if &syserr. ne 0 %then %do;
%abort cancel;
%end;
%mend runquit;
proc sort data=asdasd;
by _all_;
%runquit;
data abc;
a = 1;
b = 2;
c = 3;
%runquit;
The drawback is that you have to replace any run; or quit; statement with the call to %runquit, making the code less pretty.
A related StackOverflow question can be found here.
Also, see this discussion for other solutions.
I'm new to SAS and I'm trying to understand a code:
if MAP_ID="+" then output WORK.0201_template;
else
do;
SHEET_ID=MAP_ID;
output WORK.0201_template_f;
end;
What does it mean the MAP_ID="+"? Does it mean that it search on the table for the values where MAP_ID=+, or does it have another menaing?
Thanks
The MAP_ID="+" is a boolean expression that compares the value the variable MAP_ID to the character string literal "+". It will be true when they are the same and false otherwise.
I suspect that the main purpose of this code is to split the data into two different output datasets based on the value of MAP_ID.
It also is changing the value of SHEET_ID. That type of code also looks like something that is designed to carry forward the value of MAP_ID in a retained field SHEET_ID. If I am right then the meaning of the value of + is to keep the same sheet_id. But we would need to seem more of the code and the data to really tell.
I am not sure if I get any answer on a saturday evening, but I will give a shout :)
I am trying to automize a SAS-code which does comparison in a viariable between 2-3 companies. In each task, I will probably work with different companies, so I guess I can only enter the company names manually.
I describe macro-variables for each company, for example:
%let C1=CompanyNo1; %let C2=CompanyNo2; %let C3=CompanyNo3;
and then I only put &C1 in the remaining code. If I work for another company no later, then I only change the CompanyNo1 in the code.
However, the problem is that I may need to do the comparison between different number of companies later. If I write the code for 4 companies, and if I need to do the comparison between 2 companies in the next run, then I have to deactivate some part of the code.
So I want to write an existince check in the code like;
data _null_;
if &C3 is true then continue the run;
else if quit the code.
run;
Someone told me that I can do that with %Macro statement(he said maybe, he wasn't sure). But I am not sure how to achieve this with %Macro.
Thanks for any contribution in advance...
You can check for existence of macro variables in your code with the %SYMEXIST macro function and SYMEXIST data step function.
Finally I found an effective solution.
%Macro checking;
%let C3=CompanyNo3;
%if &SYSERR > 0 %then %do;
%goto exit;
%end;
'other data processes here';
%exit: /* Be careful, it is not semicolon */
%Mend;
So if there is no CompanyNo3, then SAS gives only a warning that CompanyNo3 is not attended. Then it goes to exit, it doesn't do other data processes. It finalizes the process without error message.
Using SAS I'm pulling data from a SQL data base using a pass through for speed as the DB's are quite large. The below code works as expected.
%let expectdate1 = '2013-07-03';*/
proc sql;
connect to ***** as abc (tdpid=***** user='****' password='*****' );
create table Searched_data as
select * from connection to dss(
SELECT *
FROM database.tablename
WHERE CAPTURE_DT >= '2013-07-01' and CAPTURE_DT <= &expectdate1
);
disconnect from abc;
quit;
The issues arises when I wan to have expectate1 parameterised.
so I replace
%let expectdate1 = '2013-07-03';*/
with
%let expectdate1 = put(Date(),YYMMDD10.);
This doesnt work and the error Im getting is something like
....WHERE CAPTURE_DT >= '2013-07-01' and CAPTURE_DT <= put(Date(),YYMMDD10) .....
So its not evaluating my date code and instead its passing the actual code to SQL and not the resultant string.
Shorack is correct that the PUT statement can not be used with %SYSFUNC, however you can use PUTN successfully.
You should simply need the following.
%LET EXPECTDATE1 = %SYSFUNC(PUTN(%SYSFUNC(DATE()),YYMMDD10.));
%put EXPECTDATE1=&EXPECTDATE1.;
SASLOG:
EXPECTDATE1=2013-08-05
Note: edited for the single quotes you need.
Let me first provide a solution that works, then explain why your approach does not work.
Use this piece of code instead:
data _NULL_;
call symput("expectdate1",cats("'",put(Date(),YYMMDD10.)),"'");
run;
The above piece of code will create your string and then put it into the expectdate1 macro variable.
So, why was your code not working?
That is because you do not make a distinction between SAS functions and SAS macro functions.
put(Date(),YYMMDD10.) are not macro functions (easily distinguished because they start with a percentage sign. -> % <-)
So SAS Macro does not evaluate it and just puts the piece of code into your SQL statement, literally.
Now there is something called the %sysfunc function. It is a macro function that will perform the enclosed normal function.
So %sysfunc(Date()) would be resolved by SAS macro before setting the macro variable expectdate.
Note that each function needs to be enclosed by the %sysfunc function, i.e.,
%let someVariable = %sysfunc(mean(max(1,3),5)); /*WRONG*/
%let someVariable = %sysfunc(mean(%sysfunc(max(1,3)),5)); /*RIGHT*/
That being said, it does not work for some SAS functions and put is one of them. That is why i provided the solution on top: use a data step to prepare it any way you like and write the result into a macro variable.
I am building macro whitch generates empty table by my own structure from other table. How to change variable type?
example from my code:
`%let vname = %sysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,varName))));
%let vtype = %sysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,varType))));
%let vformat = %sysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,varFormat))));
%if &vtype = C %then %do;
&vname=putc(&vname,&vformat);
%end;`
And it's not working... Any other ideas how to change var type?
You should be clear whether you just need empty table with some columns in a changed data type (definition) or you'd also like to actually convert the data content/values. First case should be much easier.
Because currently in
&vname=putc(&vname,&vformat);
you're trying to convert the data values, not the definition. The definition of variable is not changed and in data step actually can't be changed.
You can't create a "new" variable when there's still the original variable present inside your data step with same name.
In data step, you'd need to define new variables with different names inside data step and use RENAME (new to to original names) and DROP (original names) options on output dataset to end up with same name in output dataset (I can clarify if needed).
For just defining the empty table, PROC SQL could be easier, you need to create a code like:
proc sql;
create table lib.table (
orig_var1 type format informat label
, orig_var2 NEWTYPE format informat label
, ...
);
quit;
which can be built from dictionary.columns. This is also easier to keep original order of variables (not so easy in data step).
You just need to take care to define appropriate length for character variables based on the length of the format used if you'd like to store formatted value of numeric variable in new character variable.