SAS intnx quarter variation - date

sorry it is probably a very simple question, but I can't seem to find an answer to it.
Say, we want to create a table that contains 4 quarters back from the previous month:
%macro asd;
%let today = %sysfunc(today());
%let quarter_count_back = 4;
%let first_quarter = %sysfunc(intnx(month,&today.,-1));
proc sql;
create table quarters
(
Quarters num informat = date9. format = date9.
);
insert into quarters
%do i = 0 %to -&quarter_count_back.+1 %by -1;
values(%sysfunc(intnx(quarter,&first_quarter.,&i.)))
%end;
;
quit;
run;
%mend asd;
%asd;
run;
This code works just fine and creates a table, which starts from APR2016 and goes back in time by quarter. However, if I change the number in the 'first_quarter' line for -2, -3 etc... the code always starts from JAN2016 which just doesn't make any sense to me! For example:
%let first_quarter = %sysfunc(intnx(month,&today.,-2));
It seems logical that if I put this line in the code the table should start from MAR2016 and go back by quarter, but it does not, it starts from JAN2016.
Any ideas on what I am doing wrong here?
Thanks!

The default alignment for the INTNX function is the beginning of the interval. If you want it to go back 3 months, that's different than quarters. You can adjust these by looking at the fourth parameter of the INTNX function which controls the alignment. Options are:
Same
Beginning
End
If you want three months, try the MONTH.3 interval instead of quarter.
http://support.sas.com/documentation/cdl/en/lefunctionsref/63354/HTML/default/viewer.htm#p10v3sa3i4kfxfn1sovhi5xzxh8n.htm

Related

SAS countdown to date years/months/days

For fun I'm trying to write a macro that calculates a countdown to my approximate age of retirement.
I can't seem to get the months and days right, though.
Here's what I have so far, .
%let mybirthday = ; /* in date9 format */
DATA _NULL_;
call symput('actualend',put(intnx('years', "&mybirthday."d, 65, 'sameday'),date9.));
RUN;
DATA _NULL_;
call symput('days', INTCK('DAY',date(),"&actualend."d,'C'));
call symput('weeks', INTCK('WEEK',date(),"&actualend."d,'C'));
run;
DATA _NULL;
call symput('yrsleft', floor(&weeks./52));
run;
So far so good.
When I try to calculate months, though, using
DATA _NULL_;
call symput('mthsleft', floor((&weeks. - &years.*52)/4));
run;
I get 10, when the answer should be 9 (using Wolfram Alpha for convenience's sake).
This is my main stumbling block, can't calculate the months exactly due to the different number of days (plus leap years!).
What I'd be looking for is output like
There are V years, X months, Y weeks and Z days until $DATE.
Please help, and holy moly thank you if you made it this far :-)
You can, and should, do everything with intck / intnx to avoid issues like this. Just calculate each years/months value separately, and then increment the base date by that much. So calculate year difference, then however many years it was, make a new variable with date() + yrsleft as a value.
So if it's 10/4/2021, and you're retiring on 7/15/2049, then yrsleft is 27 - and the new variable stores 10/4/2048. Then do the same for the months - 9 months between 10/4/2048 and 7/15/2049, and that takes you to 7/4/2049, so store that in another variable. Then just subtract to get the days.
DATA _NULL_;
*calculate YRSLEFT and then store the "ending year" point in a variable;
yrsleft = intck('Year',date(),"&actualend."d,'c');
year = intnx('year',date(),yrsleft,'s');
put year= date9.;
*calculate MTHSLEFT and then store the "ending month" point in a variable;
mthsleft=intck('Month',year,"&actualend."d,'c');
month = intnx('month',year, mthsleft,'s');
*store them in macro variables;
call symputx('yrsleft', yrsleft);
call symputx('mthsleft',mthsleft);
*calculate days left directly;
call symputx('daysleft',"&actualend."d - month);
run;
%put &=yrsleft &=mthsleft &=daysleft;
I think you need CALL IS8601_CONVERT.
dur=P2Y2M14D
data _null_;
length dur $16;
start = today();
end='18DEC2023'd;
call is8601_convert('d/d','du',start,end,dur);
put dur=$n8601e.;
run;
I found the example code here.
https://www.lexjansen.com/pharmasug/2012/DS/PharmaSUG-2012-DS22-SAS.pdf

If-Else-Then with today()

i am currently trying to write some code that goes through my data and marks a number 0-12 based off the date in the "Week" column. this number appears in a new column called group which is created by the code you see below. The problem is that this column is periods all the way down and not numbers. There are no errors messages in the log so i dont know where i went wrong (im fairly new to sas). PS. the dates range from 6/17 to 9/9
data have;
set have;
if today()+84 = Week > today()+79 then group=12;
else if today()+77 = Week > today()+72 then group=11;
else if today()+70 = Week > today()+65 then group=10;
else if today()+63 = Week > today()+58 then group=9;
else if today()+56 = Week > today()+51 then group=8;
else if today()+49 = Week > today()+45 then group=7;
else if today()+42 = Week > today()+37 then group=6;
else if today()+35 = Week > today()+30 then group=5;
else if today()+28 = Week > today()+23 then group=4;
else if today()+21 = Week > today()+16 then group=3;
else if today()+14 = Week > today()+11 then group=2;
else if today()+7 = Week > today()+2 then group=1;
else if today() = Week > today()-5 then group=0;
run;
update:
the first column is called week and is a monday date that goes 12 weeks into the future. the rest of the columns are variables that i will end up summing based on the group that row is in.
ex:
week ID var2 ... var18
17jun2019 1 x x
24jun2019 1 x x
and it continues until 09sept2019.. it does this for each ID (roughly 10,000 of them) but not every id goes 12 weeks out thats why i am using the else if
i would like it to look like
week ID var2 ... var18 group
17jun2019 1 x x 0
24jun2019 1 x x 1
01july2019 1 x x 2
A full reference to SAS operators can be found in SAS help by searching SAS Operators in Expression. SAS expressions can use some operators that are relatively unique across the spectrum of coding languages. Here are some that are not typically found in newly coded SAS (at time of this post)
<> MAX operator
>< MIN operator
implied AND operator
Two comparisons with a common variable linked by AND can be condensed with an implied AND.
So the uninitiated readers of the question may misunderstand
…
if today()+35 = Week > today()+30 then group=5;
…
as incorrect, instead of recognizing it as an implied AND
…
if today()+35 = Week AND Week > today()+30 then group=5;
…
When syntactically correct, the = in the implied AND causes the expression to be true only on equality. The week value in open interval ( today()+35, today()+34 ) will never evaluate as true in the above expression. This is the likely cause of the missing values (.) you are seeing.
Why does the code exhibit non-static delta of 7 in the sequence 30,23,16,11,2,-5 ?
Should it be 30,23,16,9,2,-5.
In other words why is group 1 apparently shooting for a 5 day range [+7, +2) when all the others are 3, such as [+14, +11) ?
Why are there 2-days domains, presumed weekends, in which group is not assigned, and would thus be missing (.) ?
This type of wallpaper code is often better represented by a an arithmetic expression.
For example, presuming integer SAS date values:
group = ifn ( MOD (week-today(), 7) in (1,2)
, .
, CEIL (week-today() / 7 )
);
if not ( 0 <= group <= 12 ) then group = .; * probably dont want this but makes it compliant with OP;
Tomorrow the group value could 'wrong' because it is today() based. Consider coding a view instead of creating a permanent data set -- OR -- place meta information in the variable name group_on_20190622 = …
If you insist on wallpaper, consider using a select statement which is less prone to typing errors that can happen with errant semi-colons or missing elses.
It is not at all clear what you are trying to do. It sounds a little like you want to group observations based on how many weeks the date variable (called WEEK) is away from today's date. It might be easiest to just use the INTCK() function. That will count how many week boundary's are crossed between the two dates.
data have ;
input id week date9.;
format week date9.;
cards;
1 17jun2019
1 24jun2019
1 01jul2019
2 24jun2019
2 01jul2019
2 08jul2019
;
data want ;
set have;
group = intck('week',today(),week);
run;
You can then summarize the number of ID's per group.
proc freq data=want;
tables group;
run;
Results:
The FREQ Procedure
Cumulative Cumulative
group Frequency Percent Frequency Percent
----------------------------------------------------------
-1 1 16.67 1 16.67
0 2 33.33 3 50.00
1 2 33.33 5 83.33
2 1 16.67 6 100.00
Assuming week is date and not datetime.
data test;
do i = 1 to 30;
dt = intnx('day',today(),1*i);
output;
end;
format dt date9.;
run;
data test2;
set test;
if dt ge today() and dt le today()+7 then dt2 = 1;
else if dt ge today()+8 and dt le today()+14 then dt2 = 2;
else if dt ge today()+15 and dt le today()+21 then dt2 = 3;
else if dt ge today()+22 and dt le today()+28 then dt2 = 4;
else if dt ge today()+29 and dt le today()+35 then dt2 = 5;
/* another way */
dt3 = ceil(intck('day',today(),dt)/7);
run;
removed wrong answer.

How to reformat dates?

Hello currently my dates are stored as numeric in the form of 40547. How can I convert these to MMDDYY10.?
data SevenSec11;
set Seven11;
DateRecieved = input(put(DateRecieved, 8.), MMDDYY10.);
format DateRecieved MMDDYY10.;
run;
How to convert it depends on what the value represents. If it is dates as stored by Excel then change the offset value. If it is supposed to represent MMDDYY values then use the Z6. format in your PUT() function call.
data test;
input num ;
sasdate1 = num + '30DEC1899'd ;
sasdate2 = input(put(num ,z6.),mmddyy10.);
format num comma7. sasdate: yymmdd10. ;
cards;
40547
;
Result:
Obs num sasdate1 sasdate2
1 40,547 2011-01-04 1947-04-05
Note that using Y-M-D order for dates will eliminate confusion that truncated leading zeros can cause. It will also prevent half of your audience from confusing April 5th with May 4th.

Get the whole range of date in SAS

I know the date in SAS looks like 01Jan2017. What I want is 1 January 2017. Is there function to make it?
Thank,
Andrea
Your question is pretty vague - it's hard to tell if you just want to display it differently or store it differently.
Display it differently: Just change the format to keep the date as a number in the background (number of days since 01JAN1960) but display it how you would like.
data ds1;
date = '01JAN2017'd;
format date WORDDATX20.;
run;
Store it differently: You can use the put function to create a separate character variable containing the formatted version of your date.
data ds2;
date1 = '01JAN2017'd;
date2 = put(date1, WORDDATX20.);
run;
Answers provided by Bhavika and pm2r should also help you understand what's going on here.
your code would look like this:
data _null_;
length date1 8. string $40;
date1=today();
string = cats( date1 );
put string=;
string = cats( put(date1,date10.) );
put string=;
string = cats( put(date1,WORDDATX20.) );
put string=;
run;
and the output would be:
string=20838
string=19JAN2017
string=19 January 2017

Increment date variable

I have a SAS dataset, in which I must add a date variable, starting with a certain date (ex: July 10, 2014). For each observation, the date must increase by one day. I cannot figure out how to increment the date. Whenever I try, I get the same date for all observations.
Welcome to Stack Overflow! Let's assume your dataset looks as so:
Have
Obs Var1
1 Mazda
2 Ford
3 BMW
Want
Obs Date Var1
1 01JAN2015 Mazda
2 02JAN2015 Ford
3 03JAN2015 BMW
You can use a Sum Statement with a SAS Date Literal to accomplish this goal.
data want;
format Date date9. /* Makes date the first var, looks prettier */
set have;
if(_N_ = 1) then Date = '31DEC2014'd; /* Set initial value */
Date+1; /* Increment SAS date value by 1 day for each day */
run;
If you have not used the automatic variable N before, it's an iteration counter for each time SAS goes from the top of the data step to the bottom.
The likely reason that you are seeing the same date for each day is because you are not retaining the value you want to increment. Consider the below example program:
data WontWork;
set have;
Add_Me = 1;
/* Do loop just simulates dataset iterations */
do i = 1 to 10;
Add_Me = Add_Me + 1;
output;
end;
drop i;
run;
Explanation
Whenever SAS runs through one iteration of the data step, the Program Data Vector (PDV) resets all non-automatic variables to missing. To fix this, you must either use a Retain statement and then increment the variable, or use a Sum Statement to do the job of both retaining and summing up the variable. The Retain/Sum Statements both tell SAS to remember the last value of a variable so that it does not get reset to missing when it iterates through the data step. One unique property of the retain statement is that you can set an initial value. By default, the retain statement will initialize the variable as missing. The sum statement will always initialize a variable as missing.
data works;
retain Add_Me 0;
/* Do loop just simulates dataset iterations */
do i = 1 to 10;
Add_Me = sum(Add_Me, 1);
output;
end;
drop i;
run;
OR
data works2;
/* Do loop just simulates dataset iterations */
do i = 1 to 10;
Add_Me+1;
output;
end;
drop i;
run;
Note that the sum statement does both of these steps, and also handles missing values. Think of it as a shortcut.
I hope this resolved your problem, and again welcome to Stack Overflow!