Running specified lines of a SAS program - sas

Assume I have this program:
1 data temp;
2 set _null_;
3 run;
4
5 %put Hello world;
and I want to add two lines to it, one that runs lines 1-3 of the program, and another that runs line 5.
The second example here suggests that %include may be what I'm looking for, but %include 1-3 and %include 5 do not work. %include [path] 1-3 gets me into an infinite loop, which is undesirable.
What is the best way to accomplish this? Thanks!

EDIT: this only works for lines that have previously been submitted.
You need SPOOL option. I used RESETLINE statement to reset the line number useful when using SAS/EG. I would like to know how you intend to use it.
options spool=1;
resetline;
data temp;
set _null_;
run;
%put Hello world;
%include 1-3 / source2;
%include 5 / source2;

Macros, perhaps?
%macro one(datasetname= );
data &datasetname;
set _null_;
run;
%mend one;
%macro two(textstring= );
%put &textstring;
%mend two;
%one(datasetname= temp1);
%two(textstring= Hello world);
%one(datasetname= temp2);
%two(textstring= Hello new world);
You could feed macro variables into the process from a dataset rather than multiple macro calls. See the examples starting on p 11 here:
First & Ronk, SGI 130-30, SAS® Macro Variables and Simple Macro Programs

A single extra return could screw up your line references...If you need to add a header, or a new line of code somewhere or something are you willing to go back and fix all your line number references?
I recommend using the macro or %include options.
%macro repeat_code();
***sas code goes here;
%mend;
%repeat_code
For the %include you can create the lines inside a new file and then reference them in your code with a %include.
Depending on what those lines are actually doing, you may have other options. For example if it's a lookup or re-coding a variable I would use a format instead.

Related

Sas data step keep statement from a text file

I have a table cust_base with 1000 variables. And I have a text file contents1 containing the names of 250 variables separated by tab, that I actually need to work with. I want to do something similar to:
%include "/location/contents1.txt";
data new_cust_base(keep = &contents1.txt);
set cust_base;
run;
Is this the correct approach/syntax? Or is there a better way to go about it? I tried digging online, but couldn't find much. Thanks a lot.
You can %include source code as the interior of a keep statement.
set …;
KEEP
%include "/location/contents1.txt";
;
Working example:
data _null_;
file 'c:\temp\keeplist.tab';
put 'name' "09"x 'age' "09"x 'weight';
run;
data work.class;
set sashelp.class;
KEEP
%include 'c:\temp\keeplist.tab';
;
run;

SAS include another SAS script within a macro

I am looking to include one sas program inside the macro written in another sas program.
So:
sas_prog1.sas:
data test;
a=1;
run;
sas_prog2.sas:
%macro m2;
%include sas_prog1.sas;
%mend;
%m2;
Does the data step in sas_prog1.sas also need to be wrapped inside a macro?
No - you don't need to. When you use an %include statement, it just essentially writes out all contents in the included file at that location. In your case it just dumps the datastep code and hence it effectively becomes:
%macro m2;
data test;
a=1;
run;
%mend;
%m2;
So you should be good to go.
you can include a code in another in writting it to a temp file as character.
filename exec_code temp;
data _null_;
file exec_code;
put ' your sas instruction'
put 'your sas instruction'
run;
and in your macro use an include
%macro mymacro();
%include exec_code;
%mend;
Assuming that sas_prog1.sas is a being used as a module and you will have multiple modules for the entire code, you can simply use a %include to execute the program. There is no need to execute it inside of a macro in sas_prog2, but it can be.
contents of file saved as sas_prog1.sas:
data test;
a=1;
run;
contents of sas_prog2.sas:
%include "[prog_dir]\sas_prog2.sas";

SAS macro modification

I have two values which represent dates:
a=101 and b=103
Below is first macro saved in separate file one.sas:
%global time nmall;
%let nmall =;
%macro pmall;
%do i=&a. %to &b;
%if &i =&a. then %do;
%let nmall=&nmall.&i;
%end;
%else %let nmall=&nmall.,&i;
end;
%put (&nmall);
%mend;
%pmall;
So above pmall give me values 101,102,103.
Below is second macro:
%include “one.as”;
%macro c(a=,b=);
%let m=;
%let m1=;
%do i =&a %to &b;
%let o=&i;
proc sql;
create table new&o as select * from data where nb in(&o.);quit;
%let m =&m.date&o;
data date&o.;
set date&o.;
if pass =&o.;
run;
proc sort data=date&o.;
by flag;
end;
data output &a._&b.;
set &m;
%mend;
The above macro creates three datasets date101 date102 and date 103, then append it to output101_103.
I am trying to modify above macros in such a way that I will not use %macro and %mend approach. Below is the modified macro code:
data a_to_c;
do o=&a to &c;
output;
end;
run;
so above code will have values 101 102 103 in variable o for dataset a_to_c.
data _null_;
set a_to_c;
call execute ('create table new’||strip(o)||' as select * from data
where nb in(’||strip(o)||' );quit;’);
run;
I want to know how to do below things.
Create pmall values in a macro variable in my modified macro inside the data step data a_to_c, so that I can use it further.
How to proceed from %let m macro in the first macro code to new code which I am developing above.
Geetha:
I think you will find the macro-ization of the process to be far easier if you go from a data-centric explicit solution and proceed abstracting the salient features into macro symbols (aka variables)
The end run solution appears to be:
data output_101_to_103;
set original_data;
where nb between 101 and 103;
run;
proc sort data=output_101_to_103;
by nb flag;
run;
In which case you could code a macro that abstracts 101 to FIRST and 103 to LAST. The data sets could also be abstracted. The abstracted parts are specified as the macro parameters.
%macro subsetter(DATA=, FIRST=, LAST, OUTPREFIX=OUTPUT);
%local out;
%let out = &OUTPREFIX._&FIRST._&LAST.;
data &out;
set &DATA.;
where nb between &FIRST. and &LAST.;
* condition = "between &FIRST. and &LAST."; * uncomment if you want to carry along the condition into your output data set;
run;
proc sort data=&out;
by nb flag;
run;
%mend;
And use as
%subsetter (data=original_data, first=101, last=103, outprefix=output)
Note: If you did keep the condition variable in the output data, you WOULD NOT be able to use it directly as a source code statement in a future data step, as in if nb condition then ...
I suppose you could also pass the NB and FLAG as parameters -- but you approach a point of diminishing returns on the utility of the macro.
Macro-izing the specific example I showed doesn't make too much sense unless you need to perform a lot of different variations of FIRST and LAST in a well documented framework. Sometimes it is just better to not abstract the code and work with the specific cases. Why? Because when there are too many abstracted pieces the macro invocation is almost as long as the specific code you are generating and the abstraction just gets in the way of understanding.
If the macro is simply chopping up data and reassembling data, you might be better served rethinking the flow using where, by, and class statements and abstracting around that.
Pmall is macro variable which will have list of values separated by
commas. In my modify macro, i want to create pmall as macro variable
in the datastep data a_to_c; do o=&a to &c; output; end; run; – geetha
anand 1 min ago
To create a macro variable from within a data step using the CALL SYMPUTX() function.
data a_to_c;
length pmall $200 ;
do o=&a to &c;
pmall=catx(',',pmall,o);
output;
end;
call symputx('pmall',pmall);
drop pmall;
run;
If you really want to generate code without a SAS macro you can use CALL EXECUTE() or write the code to a file and use %INCLUDE to run it. Or for small pieces of code you could try putting the code in a macro variable, but macro variables can only contain 64K bytes.
It is really hard to tell from what you posted what code you want to generate. Let's assume that you want to generate an new dataset for each value in the sequence and then append that to some aggregate dataset. So for the first pass through the loop your code might be as simple as these two steps. First to create the proper subset in the right order and the second to append the result to the aggregate dataset.
proc sort data=nb out=date101 ;
where nb=101 ;
by flag ;
run;
proc append base=date101_103 data=date101 force;
run;
Then next two times through the loop will look the same only the "101" will be replaced by the current value in the sequence.
So using CALL EXECUTE your program might look like:
%let a=101;
%let c=103;
proc delete data=date&a._&c ;
run;
data _null_;
do nb=&a to &c;
call execute(catx(' ','proc sort data=nb out=',cats('date',nb,'),';'));
call execute(cats('where nb=',nb,';')) ;
call execute('by flag; run;');
call execute("proc append base=date&a._&c data=");
call execute(cats('date',nb));
call execute(' force; run;');
end;
run;
Writing it to a file to run via %INCLUDE would look like this:
filename code temp ;
data _null_;
file code ;
do nb=&a to &c;
put 'proc sort data=nb out=date' nb ';'
/ ' where ' nb= ';'
/ ' by flag;'
/ ';'
/ "proc append base=date&a._&c data=date" nb 'force;'
/ 'run;'
;
end;
run;
proc delete data=date&a._&c ;
run;
%include code / source2;
If the goal is to just create the aggregate dataset and you do not need to keep the smaller intermediate datasets then you could just use the same name for the intermediate dataset on each pass through the loop. That will make the code generation easier as then there is only only place that needs to change based on the current value. Also that way you only need to have two dataset names even for a sequence of 10 or 20 values. It will take less space and reduce clutter in the work library.

Code from macro execution

To create a complex analysis, I have a program that will %include some different other programs, each one with a specific purpose (mainly one for each analysis, one for macros, one for formats and so on..). All the analysis run smoothly with one click on the final program.
Basically what I want now is to implement a system that will be able to rewrite all the code in one document: exploding the macros with the correct code, exploding the %do loops with the correct datastep code, deleting the %let statements but resolving the macro variables, and so on (example at the bottom of the post).
The only idea I had was to save the log, with the MPRINT option to write everything, delete the notes after the datastep executions and other things not datastep related. But this method is really dirty, therefore I would kindly ask if you have better ideas on how to proceed.
This is an example of 2 nested programs:
main.sas
-----------------------------------------------------
/* My MAIN PROGRAM */
%let PGM=MAIN;
%put This is my &pgm. program;
%let LIB=MYLIB;
%include &LIB.(FIRST.sas);
-----------------------------------------------------
first.sas
-----------------------------------------------------
%let ind=2;
%macro ABC;
%do rk=1 %to &ind.;
data A&rk.;
MYVAR=&rk.; output;
run;
%end;
%mend; %ABC;
-----------------------------------------------------
The document that I would get is something like:
/* My MAIN PROGRAM */
%put This is my MAIN program;
data A1;
MYVAR=1; output;
run;
data A2;
MYVAR=2; output;
run;
Any suggestion? Did you ever find out this? How you solved it? Many thanks.
See Example 2 here:
http://support.sas.com/documentation/cdl/en/mcrolref/69726/HTML/default/viewer.htm#p1dhqw0i5yj2m8n15opapnwteqra.htm
You can add these options to your program:
options mfile mprint;
filename mprint 'debugmac';
And all the generated code will appear in the specified output file. This will not include %PUT statements though - in your example, your desired output includes a %PUT statement. The options above just redirect all the normal MPRINT lines to a file, so no open code or macro statements are include. You could probably find a way to append a header to the MPRINT file though, depending on your exact needs.
Having said all that, I wouldn't recommend your approach. Why would you want to flatten the program in this way? It's extremely unlikely to have any noticeable performance benefit, and will make it much harder to debug or modify the program in future.

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.