How to create multiple files in SAS? - sas

I am trying to write a macro that should create multiple external html files . Here is my code
%macro createFiles;
%let name = Jupiter*Mercury*Venus;
%let htmlTxt1 = <html><h1>Hello To ;
%let htmlTxt2 = </h1></html> ;
%let i = 1 ;
%let thisName = %scan(&name., &i.,"*") ;
%do %while (&thisName. ne ) ;
filename thisFile "C:\Users\owner\Desktop\&thisName.html";
call execute ('data _null_; file &thisFile; put &htmlTxt1 || &thisName || &htmlTxt2; run; ') ;
%let i = %eval(&i + 1 ) ;
%let thisName = %scan(&name.,&i.,"*");
%end ;
%mend ;
%createFiles
However, it does not work . Please help me
Thanks

Mostly a combination of typo's and syntax errors. SAS also has the ODS HTML destination which would be easier to use to create HTML files in my opinion.
%macro createFiles;
%let name = Jupiter*Mercury*Venus;
%let htmlTxt1 = <html><h1>Hello To ;
%let htmlTxt2 = </h1></html> ;
%let i = 1 ;
%let thisName = %scan(&name., &i.,"*") ;
%do %while (&thisName. ne ) ;
filename thisFile "C:\temp\&thisName..html";
data _null_;
file thisFile;
put "&htmlTxt1 || &thisName || &htmlTxt2";
run;
%let i = %eval(&i + 1 ) ;
%let thisName = %scan(&name.,&i.,"*");
%end ;
%mend ;
%createFiles

Related

If statement in SAS macro not always working

I'm relatively new to SAS coding, and I'm trying to write this (see the whole code down below) macro that should evaluate performances (to_worse, to_better, recoveded, etc.) of several teams.
The problem is that this rule here:
if team_&y.="&PROVIDER." and team_&i. eq "" and balance_&i. = 0 then do; regolarizzato = "regolarizzato"; end;
else if team_&i. ne team_&y. and team_&y. eq "&PROVIDER." and team_&i. ne "" then do; to_worse="to_worse"; end;
else if team_&i. = team_&y. and team_&y. eq "&PROVIDER." and balance_&y. > balance_&i. then do; to_better = "to_better"; end;
does not seem to be always properly wowking; it works most of the times, but not always, and I don't understand the reason why it's failing sometimes. Could any kindheardted fella please explain why this happens? Thank you so much in advance!
%let oggi = '07oct2022'd;
%let mese = %sysfunc (MONTH(&oggi.));
%IF &mese. < 10 %THEN %do; %let mese = 0&mese.; %end;
%let giorno = %sysfunc (DAY(&oggi.));
%let anno = %sysfunc(Year(&oggi.));
/*%IF &giorno. < 10 %THEN %do; %let giorno = 0&giorno.; %end;*/
%macro COSTI_1;
%let i = 0;
%DO i = 0 %TO &giorno.;
data COSTI_&i.;
set data.initial_db_&mese.;
format balance_&i. commax14.2;
keep contract_number team_&i. balance_&i.;
run;
%end;
%mend;
%COSTI_1;
data COSTI_db;
set COSTI_0;
run;
%macro COSTI_2;
%let i = 1;
%DO i = 1 %TO &giorno.;
PROC SQL;
CREATE TABLE COSTI_db AS
SELECT
A.*,
B.*
FROM COSTI_db AS A LEFT JOIN COSTI_&i. AS B
ON (A.contract_number = B.contract_number);
QUIT;
run;
%end;
%mend;
%COSTI_2;
data COSTI_db;
set COSTI_db;
length team $ 20;
format team $CHAR30.;
team="altro";
run;
%MACRO COSTI_PROVIDER (PROVIDER);
data COSTI_db_&Provider.;
set COSTI_db;
run;
%macro COSTI_A;
%let i = 0;
%DO i = 0 %TO &giorno.;
data COSTI_db_&Provider.;
set COSTI_db_&Provider.;
if team_&i. = "&PROVIDER." then team = "&PROVIDER.";
run;
%end;
%mend;
%COSTI_A;
DATA COSTI_&PROVIDER.;
set COSTI_db_&Provider. (where =(team="&PROVIDER."));
length to_worse $ 20;
format to_worse $CHAR30.;
length to_better $ 20;
format to_better $CHAR30.;
length regolarizzato $ 20;
format regolarizzato $CHAR30.;
to_worse="no";
to_better="no";
regolarizzato="no";
run;
%macro to_worse;
%let i = 1;
%let y = %eval(&i.-1);
%DO i = 1 %TO &giorno.;
data COSTI_&PROVIDER.;
set COSTI_&PROVIDER.;
if team_&y.="&PROVIDER." and team_&i. eq "" and balance_&i. = 0 then do; regolarizzato = "regolarizzato"; end;
else if team_&i. ne team_&y. and team_&y. eq "&PROVIDER." and team_&i. ne "" then do; to_worse="to_worse"; end;
else if team_&i. = team_&y. and team_&y. eq "&PROVIDER." and balance_&y. > balance_&i. then do; to_better = "to_better"; end;
run;
%end;
%mend;
%to_worse;
data COSTI_&PROVIDER.;
set COSTI_&PROVIDER.;
length esito_finale $ 20;
format esito_finale $CHAR30.;
format balance_affido commax12.2;
if to_worse="to_worse" then esito_finale="to_worse";
else if regolarizzato = "regolarizzato" then esito_finale="regolarizzato";
else if to_better = "to_better" then esito_finale = "to_better";
else if team_&giorno. = "&PROVIDER." then esito_finale = "in_gestione_oggi";
if richiamo_o_repo = "&PROVIDER." and inflows < -1 then esito_finale = "richiamo";
if richiamo_o_repo = "&PROVIDER." and to_normal > 1 then esito_finale = "repo";
if team_0 = "&PROVIDER." then balance_affido = balance_0;
else balance_affido = -1;
drop INFLOWS TO_NORMAL RICHIAMO_O_REPO;
run;
%macro COSTI_B;
%let i = 0;
%DO i = 1 %TO &giorno.;
data COSTI_&PROVIDER.;
set COSTI_&PROVIDER.;
if team_&i. = "&PROVIDER." and balance_affido = -1 then balance_affido=balance_&i.;
run;
%end;
%mend;
%COSTI_B;
proc sql;
create table RIEPILOGO_&PROVIDER.
as select esito_finale, sum(balance_affido) as somma_balance_affido
from COSTI_&PROVIDER.
group by esito_finale;
quit;
data RIEPILOGO_&PROVIDER.;
set RIEPILOGO_&PROVIDER.;
format somma_balance_affido commax12.2;
run;
%MEND;
%COSTI_PROVIDER(TEAM_A);
%COSTI_PROVIDER(TEAM_B);
%COSTI_PROVIDER(TEAM_C);
%COSTI_PROVIDER(TEAM_D);
The macro that is generating that IF statement seems to have few issues.
First the macro variable Y is always going to be 0 since it is set to %eval(1-1). Did you intend Y to always be one less than I? If so move that %LET statement inside the %DO loop.
Second you keep reading and writing the same dataset over and over. You should probably move the %DO loop so that it can all be done with one data step.
%macro to_worse;
%local i y ;
data COSTI_&PROVIDER.;
set COSTI_&PROVIDER.;
%do i = 1 %TO &giorno.;
%let y = %eval(&i.-1);
if team_&i. eq "" and team_&y. eq "&PROVIDER." and balance_&i. = 0 then regolarizzato = "regolarizzato";
else if team_&i. ne team_&y. and team_&y. eq "&PROVIDER." and team_&i. ne "" then to_worse="to_worse";
else if team_&i. eq team_&y. and team_&y. eq "&PROVIDER." and balance_&y. > balance_&i. then to_better = "to_better";
%end;
run;
%mend to_worse;
And finally your set of IF/THEN/ELSE conditions do not cover all of the possible combinations of values of TEAM_&I, TEAM_&Y, BALANCE_&I and BALANCE_&Y. Are you sure that the situations where you think it is not doing what you want are not just the situations that fall into one of those uncovered combinations?

Write out variables in a loop to a dataset

I want to assign a string to a variable in a loop and write out the variable to a dataset on each iteration.
Here is the code that prints out each variable
%macro t_size(inlib=,inds=);
%let one_gig = 5000;
proc sql noprint;
select ceil((nobs*obslen)/&one_gig) into :tsize
from sashelp.vtable where libname=upcase("&inlib") and memname=upcase("&inds");
quit;
%let no_of_tables=%eval(%sysfunc(int(&tsize)));
%if (&tsize gt 1) %then
%do i = 1 %to &no_of_tables;
%put &inds._&i.;
%end;
%else
%do;
%put &inds.;
%end;
%mend;
%t_size(inlib=SASHELP,inds=SHOES);
run;
This produces the required output:
SHOES_1
SHOES_2
SHOES_3
SHOES_4
SHOES_5
SHOES_6
SHOES_7
Instead of printing the variables out to the log I want to write them to a new, empty dataset.
It appears you are attempting to split a data set FOO into N one_gig pieces FOO_1 to FOO_N. Your first step also appears to be creating the FOO target table names. Computing the split names within a DATA step will save the computed names.
Example:
%macro make_split_names(data=, out=split_names, splitsize=5000);
%local lib mem;
%let syslast = &data;
%let lib = %scan(&data,1,.);
%let mem = %scan(&data,2,.);
data parts;
ds = open ('sashelp.cars');
nobs = attrn(ds, 'NOBS');
lrecl = attrn(ds, 'LRECL');
ds = close(ds);
do n = 1 to ceil ( nobs * lrecl / &splitsize );
name = catx("_", "&mem", n);
OUTPUT;
end;
keep name;
run;
%mend;
%make_split_names (data=sashelp.cars)
If you want a dataset then replace your last block of macro logic with a data step.
data member_list ;
length memname $32 ;
if &no_of_tables > 1 then do i=1 to &no_of_tables;
memname=catx('_',"&inds",i);
output;
end;
else do;
memname="&inds";
output;
end;
keep memname;
run;
Solution:
%macro t_size(inlib=,inds=);
%let one_gig = 5000;
proc sql noprint;
select ceil((nobs*obslen)/&one_gig) into :tsize
from sashelp.vtable where libname=upcase("&inlib") and memname=upcase("&inds");
quit;
%let no_of_tables=%eval(%sysfunc(int(&tsize)));
data temp;
length temp $100;
%if (&tsize gt 1) %then
%do i = 1 %to &no_of_tables;
temp= "&inds._&i.";
output;
%end;
%else
%do;
temp= "&inds.";
output;
%end;
run;
%mend;
%t_size(inlib=SASHELP,inds=SHOES);
run;
Just add data step named temp, where input in variable temp.
Output:
+---------+
| temp |
+---------+
| SHOES_1 |
| SHOES_2 |
| SHOES_3 |
| SHOES_4 |
| SHOES_5 |
| SHOES_6 |
| SHOES_7 |
+---------+

how to create subsets of variables in SAS using a criteria

I am trying to run a regression with two independent variables automatically selected (meeting a certain criterion) from a variable list. For example, my variable list is:
Var1 Var2 Var3 Var4 Var5
I am trying to run 10 regressions using the pattern:
outcomeVar = var1 var2
OutcomeVar = var1 var3
.
.
.
OutcomeVar = var2 var3
.
.
.
OutcomeVar = var4 Var5
I am trying to generate a macro that will contain a loop that will automatically build these regressions. I am trying to use the %scan function to generate this loop but cannot formulate a criteron for variable selection.
A nested loop is one option :
%MACRO COMBI ;
%LET NVAR = 5 ;
%DO X = 1 %TO %EVAL(&NVAR - 1) ;
%DO Y = %EVAL(&X + 1) %TO &NVAR ;
%LET OUTCOMEVAR = VAR&X VAR&Y ;
%PUT &OUTCOMEVAR ;
/* do something else with outcomevar */
%END ;
%END ;
%MEND ;
%COMBI ;
If your variables aren't actually numbered and sequential, you'd need to adopt a slightly different approach :
%MACRO COMBI ;
%LET VARLIST = somevar thisvar thatvar varx vary ;
%LET NVAR = %SYSFUNC(countw(&VARLIST)) ;
%DO X = 1 %TO %EVAL(&NVAR - 1) ;
%DO Y = %EVAL(&X + 1) %TO &NVAR ;
%LET OUTCOMEVAR = %SYSFUNC(scan(&VARLIST,&X)) %SYSFUNC(scan(&VARLIST,&Y)) ;
%PUT &OUTCOMEVAR ;
/* do something else with outcomevar */
%END ;
%END ;
%MEND ;
%COMBI ;

%DROPMISS - SAS

I've been reading up on how to DROP variables from my dataset that have null values in every observation - it seems the best way to do this is using the %DROPMISS macro function - however I'm getting an error msg - below is the code I'm trying and the error msg
Code
%DROPMISS (DSIN=dataset1, DSOUT=dataset2);
Log
4665 %DROPMISS (DSIN=dataset1, DSOUT=dataset2);
-
180
WARNING: Apparent invocation of macro DROPMISS not resolved.
ERROR 180-322: Statement is not valid or it is used out of proper order.
You need to define the dropmiss macro before you use it.
You can find it here in the appendix (page3)
http://support.sas.com/resources/papers/proceedings10/048-2010.pdf
Or better formatted here:
/******************/
options nomprint noSYMBOLGEN MLOGIC;
/****************************/
%macro DROPMISS( DSNIN /* name of input SAS dataset
*/
, DSNOUT /* name of output SAS dataset
*/
, NODROP= /* [optional] variables to be omitted from dropping even if
they have only missing values */
) ;
/* PURPOSE: To find both Character and Numeric the variables that have only
missing values and drop them if
* they are not in &NONDROP
*
* NOTE: if there are no variables in the dataset, produce no variables
processing code
*
*
* EXAMPLE OF USE:
* %DROPMISS( DSNIN, DSNOUT )
* %DROPMISS( DSNIN, DSNOUT, NODROP=A B C D--H X1-X100 )
* %DROPMISS( DSNIN, DSNOUT, NODROP=_numeric_ )
* %DROPMISS( DSNIN, DSNOUT, NOdrop=_character_ )
*/
%local I ;
%if "&DSNIN" = "&DSNOUT"
%then %do ;
%put /------------------------------------------------\ ;
%put | ERROR from DROPMISS: | ;
%put | Input Dataset has same name as Output Dataset. | ;
%put | Execution terminating forthwith. | ;
%put \------------------------------------------------/ ;
%goto L9999 ;
%end ;
/*###################################################################*/
/* begin executable code
/*####################################################################/
/*===================================================================*/
/* Create dataset of variable names that have only missing values
/* exclude from the computation all names in &NODROP
/*===================================================================*/
proc contents data=&DSNIN( drop=&NODROP ) memtype=data noprint out=_cntnts_( keep=
name type ) ; run ;
%let N_CHAR = 0 ;
%let N_NUM = 0 ;
data _null_ ;
set _cntnts_ end=lastobs nobs=nobs ;
if nobs = 0 then stop ;
n_char + ( type = 2 ) ;
n_num + ( type = 1 ) ;
/* create macro vars containing final # of char, numeric variables */
if lastobs
then do ;
call symput( 'N_CHAR', left( put( n_char, 5. ))) ;
call symput( 'N_NUM' , left( put( n_num , 5. ))) ;
end ;
run ;
/*===================================================================*/
/* if there are no variables in dataset, stop further processing
/*===================================================================*/
%if %eval( &N_NUM + &N_CHAR ) = 0
%then %do ;
%put /----------------------------------\ ;
%put | ERROR from DROPMISS: | ;
%put | No variables in dataset. | ;
%put | Execution terminating forthwith. | ;
%put \----------------------------------/ ;
%goto L9999 ;
%end ;
/*===================================================================*/
/* put global macro names into global symbol table for later retrieval
/*===================================================================*/
%LET NUM0 =0;
%LET CHAR0 = 0;
%IF &N_NUM >0 %THEN %DO;
%do I = 1 %to &N_NUM ;
%global NUM&I ;
%end ;
%END;
%if &N_CHAR > 0 %THEN %DO;
%do I = 1 %to &N_CHAR ;
%global CHAR&I ;
%end ;
%END;
/*===================================================================*/
/* create macro vars containing variable names
/* efficiency note: could compute n_char, n_num here, but must declare macro names
to be
global b4 stuffing them
/*
/*===================================================================*/
proc sql noprint ;
%if &N_CHAR > 0 %then %str( select name into :CHAR1 - :CHAR&N_CHAR from
_cntnts_ where type = 2 ; ) ;
%if &N_NUM > 0 %then %str( select name into :NUM1 - :NUM&N_NUM from
_cntnts_ where type = 1 ; ) ;
quit ;
/*===================================================================*/
/* Determine the variables that are missing
/*
/*===================================================================*/
%IF &N_CHAR > 1 %THEN %DO;
%let N_CHAR_1 = %EVAL(&N_CHAR - 1);
%END;
Proc sql ;
select %do I= 1 %to &N_NUM; max (&&NUM&I) , %end; %IF &N_CHAR > 1 %THEN %DO;
%do I= 1 %to &N_CHAR_1; max(&&CHAR&I), %END; %end; MAX(&&CHAR&N_CHAR)
into
%do I= 1 %to &N_NUM; :NUMMAX&I , %END; %IF &N_CHAR > 1 %THEN %DO;
%do I= 1 %to &N_CHAR_1; :CHARMAX&I,%END; %END; :CHARMAX&N_CHAR
from &DSNIN;
quit;
/*===================================================================*/
/* initialize DROP_NUM, DROP_CHAR global macro vars
/*===================================================================*/
%let DROP_NUM = ;
%let DROP_CHAR = ;
%if &N_NUM > 0 %THEN %DO;
DATA _NULL_;
%do I = 1 %to &N_NUM ;
%IF &&NUMMAX&I =. %THEN %DO;
%let DROP_NUM = &DROP_NUM %qtrim( &&NUM&I ) ;
%END;
%end ;
RUN;
%END;
%IF &N_CHAR > 0 %THEN %DO;
DATA _NULL_;
%do I = 1 %to &N_CHAR ;
%IF "%qtrim(&&CHARMAX&I)" eq "" %THEN %DO;
%let DROP_CHAR = &DROP_CHAR %qtrim( &&CHAR&I ) ;
%END;
%end ;
RUN;
%END;
/*===================================================================*/
/* Create output dataset
/*===================================================================*/
data &DSNOUT ;
%if &DROP_CHAR ^= %then %str(DROP &DROP_CHAR ; ) ; /* drop char variables
that
have only missing values */
%if &DROP_NUM ^= %then %str(DROP &DROP_NUM ; ) ; /* drop num variables
that
have only missing values */
set &DSNIN ;
%if &DROP_CHAR ^= or &DROP_NUM ^= %then %do;
%put /----------------------------------\ ;
%put | Variables dropped are &DROP_CHAR &DROP_NUM | ;
%put \----------------------------------/ ;
%end;
%if &DROP_CHAR = and &DROP_NUM = %then %do;
%put /----------------------------------\ ;
%put | No variables are dropped |;
%put \----------------------------------/ ;
%end;
run ;
%L9999:
%mend DROPMISS ;

SAS Is it possible not to use "TO" in a do loop in MACRO?

I used to use a %do ... %to and it worked fine , but I when I tried to list all character values without %to I got a message ERROR: Expected %TO not found in %DO statement
%macro printDB2 ;
%let thisName = ;
%do &thisName = 'Test1' , 'Test2' , 'Test3' ;
proc print data=&thisName ;
run ;
%end ;
%mend printDB2 ;
I know how to complete this task using %to or %while . But I am curious is it possible to list all character values in the %do ? How can I %do this ?
If your goal here is to loop through a series of character values in some macro logic, one approach you could take is to define corresponding sequentially named macro variables and loop through those, e.g.
%let mvar1 = A;
%let mvar2 = B;
%let mvar3 = C;
%macro example;
%do i = 1 %to 3;
%put mvar&i = &&mvar&i;
%end;
%mend example;
%example;
Alternatively, you could scan a list of values repeatedly and redefine the same macro var multiple times within your loop:
%let list_of_values = A B C;
%macro example2;
%do i = 1 %to 3;
%let mvar = %scan(&list_of_values, &i, %str( ));
%put mvar = &mvar;
%end;
%mend example2;
%example2;
I've explicitly specified that I want to use space as the only list delimiter for scan - otherwise SAS uses lots default delimiters.