Can any body help me with this do loop? - sas

Any one can help me to see what wrong in this do loop? I want to generate outm with 2,3,4 and 5. I need to create a do loop to handle it but I failed.
data Dmatrix5;
input x1 x2 x3 x4 x5;
cards;
0 . . . .
2 0 . . .
15 0 . .
4 3 5 0 .
5 8 7 6 0
;
run;%macro loop
%Do outm=5%to2;
%let inm=5;
%let outm=%eval(&inm-1);
data Dmatrix&outm;
retain minv small large;
array cor{&inm,&inm} _temporary_;
do i=1 to &inm;
set Dmatrix&inm;
array a[&inm] x1--x&inm;
array op[&outm];
do j=1 to &inm;
cor[i,j]=a[j];
end;
end;`
do i=1 to &inm;
if i>1 then do;
do j=1 to i-1;
m=cor{i,j};
if i=2 & j=1 then do;
minv=m;
small=i;
large=j;
delrc=j;
end;
else do;
if minv > m then do;
minv=m;
delrc=i;
if i>j then do;
small=j;
large=i;
end;
else do;
small=i;
large=j;
end;
end;
end;
end;
end;
end;
array out{&inm};
do i=1 to &inm;
do j=1 to i;
if large=i and small=j then out[j]=99999;
else if large=j and small=i then out[j]=99999;
else if j=i then out[j]=0;
else do;
if small=i then do;
if small>j and large >j then do;
if cor[small,j]<cor[large,j] then out[j]=cor[small,j];
else out[j]=cor[large,j];
end;
else if small>j and large<j then do;
if cor[small,j]<cor[j,large] then out[j]=cor[small,j];
else out[j]=cor[j,large];
end;
end;
else if small=j then do;
if small<i and large<i then do;
if cor[i,small]<=cor[i,large] then out[j]=cor[i,small];
else out[j]=cor[i,large];
end;
else if small<i and large>i then do;
if cor[i,small]<=cor[large,i] then out[j]=cor[i,small];
else out[j]=cor[large,i];
end;
end;
else if large=j or large =i then
out[j]=99999;
else out[j]=cor[i,j];
end;
end;
n=1;
do k=1 to &inm;
if k^=delrc then do;
op[n]=out[k];
n=n+1;
end;
end;
if delrc^=i then output;
end;
keep op1-op&outm;
run;
data Dmatrix&outm;
set Dmatrix&outm;
x1=op1;
x2=op2;
keep x1-x2;
run;
%end;
%Mend loop;
%loop;

%Do outm=5%to2;
will never iterate. You need to use %BY -1 to descend by 1. Plenty of documentation.
%macro example1;
%local index;
%do index = 5 %to 2 %by -1;
%put &=index;
%end;
%mend;
%example1
Or you can compute a descending index yourself.
%macro example2;
%local iteration index;
%do iteration = 1 %to 4;
%let index = %eval(6-&iteration);
%put &=index;
%end;
%mend;
%example2

Related

Count and generate a constant in SAS

For each id count the number of qc=fails, generate a new column final with all values as "repeat" if any one id has 2 or more than 2 fails, else pass. Repeat if count is greater than equal to 2. For some reason the if-else statement does not work. Instead of the final being a repeat for count greater than equal to 2, it is displaying it as pass.dataset
%let repeat_flag = 0;
%let pass_flag = 0;
data _null_;
set exp;
if count ge 2 then call symputx ('repeat_flag',1);
else call symputx ('pass_flag',1);
stop;
run;
%if &repeat_flag %then %do;
data exp;
set exp;
Final = 'REPEAT';
run;
%end;
%if &pass_flag %then %do;
data exp;
set exp;
Final = 'PASS';
run;
%end;
%put &repeat_flag;
%put &pass_flag;
If you want to test if any observation has COUNT larger than one then use a pattern like this instead. Set the macro variable to the FALSE condition and then set it TRUE when the condition is met.
%let pass_flag=PASS;
data _null_;
set exp;
if count ge 2 then do;
call symputx ('pass_flag','REPEAT');
stop;
end;
run;
data want;
set exp;
length final $6;
final="&pass_flag" ;
run;

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?

How can I transform this code into macro?

So I'd like to do a macro code mixed with proc sql and data step. I have the following code in SAS:
data work.calendar;
set work.calendar;
if business_day_count^=-1 then do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
end;
else
business_day_count = -1;
run;
I'd like to put it into macro code, but it doesn't work.
My macro code:
%macro1();
data work.job_calendar;
set work.job_calendar;
%if business_day_count^=-1 %then %do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
%end;
else
business_day_count = -1;
run;
%mend;
The whole code is:
%macro update_day(date);
proc sql;
update work.job_calendar
set business_day_count =
case when datepart(calendar_date) = "&date"d then -1
else business_day_count
end;
quit;
proc sql;
update work.job_calendar
set status_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
proc sql;
update work.job_calendar
set rundate_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
proc sql;
update work.job_calendar
set daily_rundate_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
proc sql;
update work.job_calendar
set weekly_rundate_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
proc sql;
update work.job_calendar
set monthly_rundate_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
data work.job_calendar;
set work.job_calendar;
if business_day_count^=-1 then do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
end;
else
business_day_count = -1;
%mend;
The error code is: ERROR 180 - 322 Statement is not valid or it is used out of proper order. I don't know what I'm doing wrong.
You're mixing data step and macro code. Not sure what you're trying to achieve so no idea on what to propose but removing the %IF/%THEN will allow your code to work.
%macro1();
data work.job_calendar;
set work.job_calendar;
if business_day_count^=-1 then do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
end;
else
business_day_count = -1;
run;
%mend;
%macro1;
Here is a tutorial on converting working code to a macro and one on overall macro programming.
To define a macro you need to use the %MACRO statement. Also why did you change lines 3 and 7 from data statements to macro statements?
That code cannot work. First the %IF is always true since the string business_day_count is never going to match the string -1. Second you have an else statement without any previous if statement.
Try something like this instead.
%macro macro1();
data work.job_calendar;
set work.job_calendar;
if business_day_count^=-1 then do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
end;
else business_day_count = -1;
run;
%mend macro1;

Assigning let variables to WHERE statement using DATA NULL

hi I am trying to use DATA NULL step to assign value to variable based on different criteria. This variable from NULL statement will be assigned to WHERE statement in the following DATA step.
Ideally if I run it today(Thursday which is 5) the code should return 30APR2019 for both the variables. But my code throws only the variable value in the LAST-IF- statement.
data _null_;
if weekday(today()) = 5 then do;
%let exc_st_day = '30APR2019'd;
%let exc_en_day = '30APR2019'd;
end;
else if weekday(today()) = 6 then do;
%let exc_st_day = '01MAY2019'd;
%let exc_en_day = '01MAY2019'd;
end;
else if weekday(today()) = 2 then do;
%let exc_st_day = '02MAY2019'd;
%let exc_en_day = '02MAY2019'd;
end;
else if weekday(today()) = 3 then do;
%let exc_st_day = '03MAY2019'd;
%let exc_en_day = '03MAY2019'd;
end;
else if weekday(today()) = 4 then do;
%let exc_st_day = '04MAY2019'd;
%let exc_en_day = '06MAY2019'd;
end;
%put &exc_st_day &exc_en_day;
run;
You need to use CALL SYMPUTX() to create macro variables, not %LET within a data step.
if weekday(today()) = 5 then do;
call symputx('exc_st_day', '30APR2019'd);
call symputx('exc_en_day', '30APR2019'd);
end;
Macro code is evaluated BEFORE the SAS code that it generates runs. So you told SAS to run this code:
%let exc_st_day = '30APR2019'd;
%let exc_en_day = '30APR2019'd;
%let exc_st_day = '01MAY2019'd;
%let exc_en_day = '01MAY2019'd;
%let exc_st_day = '02MAY2019'd;
%let exc_en_day = '02MAY2019'd;
%let exc_st_day = '03MAY2019'd;
%let exc_en_day = '03MAY2019'd;
%let exc_st_day = '04MAY2019'd;
%let exc_en_day = '06MAY2019'd;
%put &exc_st_day &exc_en_day;
data _null_;
if weekday(today()) = 5 then do;
end;
else if weekday(today()) = 6 then do;
end;
else if weekday(today()) = 2 then do;
end;
else if weekday(today()) = 3 then do;
end;
else if weekday(today()) = 4 then do;
end;
run;
If you want to create macro variable values from a data step use the CALL SYMPUTX() function. Or if your really need to insert leading and/or trailing spaces into the macro variable value use the older CALL SYMPUT() function.

SAS - defining variable as column sum and bootstrapping

I have a chunk to bootstrap a dataset.
%let iter = 2;
%let seed = 777;
data work.seg;
input segment $3. prem loss;
datalines;
AAA 5000 0
AAA 3000 12584
AAA 200 245
AAA 500 678
;
data work.test;
do i=1 to &iter;
sumprem=0;
do _n_=1 to 1000000 until (sumprem>=8700);
row_i=int(ranuni(&seed)*n)+1;
set work.seg point=row_i nobs=n;
sumprem + prem;
output;
end;
end;
stop;
run;
It works, but I have a few questions.
How can I make the 400 number dynamic... I want (sumprem >= 8700) to be (sumprem >= &threshold) where &threshold is the sum of the prem column.
Is it correct how I am passing the &seed? Or should (&seed) be replaced with something like (&seed + _n_)?
How can I make the last data step into a macro... something like below, but I haven't gotten anything to work.
%macro boot(data, iter, seed);
%do i=1 %to &iter;
sumprem=0;
%do _n_=1 %to 1000000 %until (sumprem>=8700);
row_i=int(ranuni(&seed)*n)+1;
set work.seg point=row_i nobs=n;
sumprem + prem;
output;
%end;
%end;
%mend;
I assume you want to calculate the sum of prem from work.seq?
proc sql noprint ;
select sum(prem) format=best32. into :threshold trimmed from seg ;
quit;
Your macro code is confusing macro logic, which can be used to generate code, and data step logic which is what operates on the actual data. To make it into a macro just use the same macro variable names for the names of the parameters to the macro and leave the code the same.
%macro boot(in,out, iter, seed);
data &out;
do until (eof);
set &in end=eof;
threshold + prem ;
end;
do i=1 to &iter;
sumprem=0;
do _n_=1 to 1000000 until (sumprem>=threshold);
row_i=int(ranuni(&seed)*n)+1;
set &in point=row_i nobs=n;
sumprem + prem;
output;
end;
end;
stop;
run;
%mend boot;