SAS Output Returning Different Value - sas

I have the following SAS code that's using data from the American Communities Survey from the Census:
if year2<2018 then do; if gasp=3 then gasp2=0; else if gasp>3 then gasp2=gasp; end;
else if year2>=2018 then do; if gasfp="3" then gasp2=0; else if gasfp="4" then gasp2=gasp; end;
if year2<2018 then do; if elep=2 then elep2=0; else if elep>2 then elep2=elep; end;
else if year2>=2018 then do; if elefp="2" then elep2=0; else if elefp="3" then elep2=elep; end;
if year2<2018 then do; if watp=2 then watp2=0; else if watp>2 then watp2=watp; end;
else if year2>=2018 then do; if watfp="2" then watp2=0; else if watfp="3" then watp2=watp; end;
if year2<2018 then do; if fulp=2 then fulp2=0; else if fulp>2 then fulp2=fulp; end;
else if year2>=2018 then do; if fulfp="2" then fulp2=0; else if fulfp="3" then fulp2=fulp; end;
GRNTP=gasp2 + elep2 + watp2/12 + fulp2/12;
if year2 = 2019 then GRNTP;
It produces the following output:
GRNTP
698966477
However, when I sum the value of of the individual columns they return a different result:
GASP2
ELEP2
WATP2/12
FULP2/12
TOTAL
155826234
496821614
126975649
26472227
806095724
Can someone explain why this is happening, and how to fix?

Related

Several if statements

I want to flag Komp and Bauspar if either one of them is <1 with -, >1 with + and if one of them is blank --> no flag.
Tried the following, but it produces with two 2022_Bauspar_flag columns somehow?
Can you give me hint?
Thanks a lot.
Kind regards,
Ben
%macro target_years2(table,type);
%local name_Bauspar name_Komp;
data &table ;
set work.&table;
%let name_Komp = "2022_ZZ_Komp"n;
%let name_Bauspar = "2022_ZZ_Bauspar"n;
&name_Komp = (1+("2022_Komposit"n-"2022_Komposit_Ziel"n)/"2022_Komposit_Ziel"n);
&name_Bauspar = (1+("2022_Bausparen"n-"2022_Bausparen_Ziel"n)/"2022_Bausparen_Ziel"n);
/*create ZZ_flags*/
if &name_Komp > 1 THEN do;
"2022_ZZ_Komp_flag"n = '+';
end;
else if &name_Komp < 1 and &name_Komp <> . THEN do;
"2022_ZZ_Komp_flag"n = '-';
end;
else if &name_Bauspar > 1 THEN do;
"2022_ZZ_Baupar_flag"n = '+';
end;
else if &name_Bauspar < 1 and &name_Bauspar <> . THEN do;
"2022_ZZ_Bauspar_flag"n = '-';
end;
else do;
end;
run;
%mend;
%target_years2(Produktion_temp,Produktion)
Difficult to help you as you do not provide any output or detailed explanation of what is wrong.
Note that if you want to compute both columns for each observations you would need to split your if statement. The second IF condition is not evaluated when the first IF condition is true.
I understand you want to compute two derived columns 2022_ZZ_Komp_flag and 2022_ZZ_Bauspar_flag with the following condition:
if associated macro variable &name_ > 1 then flag is +
if associated macro variable &name_ < 1 then flag is -
if associated macro variable &name_ = . then flag is missing
With the following dataset
data have;
input zz_komp zz_baupar;
cards;
0.9 1.1
1.1 0.8
. 2
0.8 .
;
The following code
data want;
set have;
"2022_ZZ_Komp_flag"n = ifc(zz_komp > 1, '+', '-');
"2022_ZZ_Baupar_flag"n = ifc(zz_baupar > 1, '+', '-');
if missing(zz_komp) then "2022_ZZ_Komp_flag"n = '';
if missing(zz_baupar) then "2022_ZZ_Baupar_flag"n = '';
run;
Produces
Is it the expected result?
You have a typo in your code. You assign to Baupar_flag in one case, and Bauspar_flag in the other
else if &name_Bauspar > 1 THEN do;
"2022_ZZ_Baupar_flag"n = '+';
------
end;
else if &name_Bauspar < 1 and &name_Bauspar <> . THEN do;
"2022_ZZ_Bauspar_flag"n = '-';
-------

Double output for long to wide transformation of sequential data

The data is as follows:
Random characters so the bot lets me post my edit:sdnasdinaiwefjaepofj
ID|Character_date|Flag|SASDATE
A |2012_01 |0/1 |MONYY
A |2012_02 |0/1 |MONYY
.................
F |2012_02 |0/1 |MONYY
I want to transform it to be wide but with 12 months intervals.
So for each date there would be an account with 12 columns each indicating a date of flag activation with a horizon of 12 months
ID|Character_date|SASDATE|Flag_actived_date1 |Flag_actived_date2 |Flag_actived_date3
|Flag_actived_date4 |Flag_actived_date5 |Flag_actived_date6 |Flag_actived_date7 |...
A |2012_01 |MONYY |(if flag 1 the date)|....
B |2012_01 |MONYY |(if flag 1 the date)|....
C |2012_01 |MONYY |(if flag 1 the date)|....
...............
A |2012_02 |MONYY |(if flag 1 the date)|....
B |2012_02 |MONYY |(if flag 1 the date)|....
C |2012_02 |MONYY |(if flag 1 the date)|....
data pd_base_ttd2;
set pd_base_std end=eof;
format ttd best12. def_count best12.;
ARRAY def_dates{0:13} def_dates1-def_dates14;
;retain count def_dates1-def_dates14 def_count;
by descending credit_id ;
if first.credit_id then do;
count=0;
def_count=0;
do i=0 to 13;def_dates{i}=0;end;
;end;
if default_flag=1 then do;
def_dates{mod(count,12)}=date_obs;
count=count+1;
def_count=def_count+1;
;end;
else if default_flag=0 then count=count+1;
if last.credit_id or mod(count,12)=11 then output;
run;
DATA pd_base_std;
if 0 then set pd_base_ttd2(keep=credit_id YYYY_mm) pd_base_std;
if _n_ eq 1 then do;
declare hash h_cf(dataset:'pd_base_ttd2',hashexp:15, multidata:'Y');
h_cf.defineKey('credit_id','YYYY_mm');
h_cf.defineData('credit_id','YYYY_mm');
h_cf.defineDone();
end;
set pd_base_std;
rc_cf = h_cf.find();
do while(rc_cf=0);
rc_cf=h_cf.find_next();
end;
if rc_cf ne 0 then output;
drop rc_cf;
run;
proc sort data=pd_base_std ; by descending credit_id ; run;
data pd_base_all; set pd_base_ttd2 pd_base_all; run;
And repeat that over and over.
What I want is something like this.:
data pd_base_ttd2 pd_base_std ;
do untill(eof);
set pd_base_std end=eof;
format ttd best12. def_count best12.;
ARRAY def_dates{0:13} def_dates1-def_dates14;
;retain count def_dates1-def_dates14 def_count;
by descending credit_id ;
if first.credit_id then do;
count=0;
def_count=0;
do i=0 to 13;def_dates{i}=0;end;
;end;
if default_flag=1 then do;
def_dates{mod(count,12)}=date_obs;
count=count+1;
def_count=def_count+1;
;end;
else if default_flag=0 then count=count+1;
if last.credit_id or mod(count,12)=11 then
do;
output to pd_base_ttd2;
rc=1;
end;
end;
if rc=1 then delete;
do _N_=1 by 1 untill(last.credit_id);
set pd_base_std end=ef;
by descending credit_id;
output pd_base_std ;
end;
run;
I over complicated my approaches, probably eager to test out stuff I read about.
data pd_base_ttd2 pd_base_std ;
do _N_=1 by 1 until(last.credit_id);
set lgd.pd_base_std end=eof;
format ttd best12. def_count best12.;
ARRAY def_dates{0:13} def_dates1-def_dates14;
retain count def_dates1-def_dates14 def_count;
by descending credit_id ;
if first.credit_id then do;
count=0;
def_count=0;
do i=0 to 13;def_dates{i}=0;end;
;end;
if default_flag=1 then do;
def_dates{mod(count,12)}=date_obs;
count=count+1;
def_count=def_count+1;
;end;
else if default_flag=0 then count=count+1;
if last.credit_id or mod(count,12)=11 then output pd_base_ttd2;
else output pd_base_std ;
end;
run;
Now the question is how to this without a macro loop but instead making sas read the set again and again? Like: ok you reached the end, now start over, it would also have to be able to keep the descending order of both credit_id and YYYY_MM.

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.

Can any body help me with this do loop?

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