SAS Date Functions: Quarterly Equivelant of Day Function - sas

I often use the day function in order to control date parameters within queries.
With the data step below I can call the beginning of the current month &bom based on whether or not the day of the month is le to the 8th of each month (in which that case we want &bom set to the 1st of the previous month), otherwise set &bom to the 1st of the current month.
data _null_;
call symput('current'," '" || put(intnx('day',today(),0),yymmdd10.) || "'");
call symput('bom'," '" || put(intnx('month',today(),0,'b'),yymmdd10.) || "'");
call symput('end'," '" || put(intnx('day',today(),-8,'e'),yymmdd10.) || "'");
if day(today()) le 8 then do;
call symput('bom'," '" || put(intnx('month',today(),-1,'b'),yymmdd10.) || "'");
end;
run;
%put &bom &end &current;
262 %put &bom &end &current;
'2015-12-01' '2015-12-30' '2016-01-07'
It would seem simple to apply this logic to a "quarterly type" condition. So - If the (sequential) day of the quarter is less than 8 days after the last day of the quarter, your &boq (beginning of quarter) value would be the first day of the LAST quarter'2015-10-01', but the qtr function creates values based on the quarter 1-4, not the "number" representing the sequential day of the quarter.
Is there a function that can operate on the number of days on a quarterly level, much like the day function operating on a monthly level?
My initial attempt was wrapping functions...no success...
qtr_day = day(qtr(today()));

The trick is to use SAS date, since SAS dates are numbers, so you can find the date boundary and add/subtract 8 as desired to increment. Or you can nest intnx functions on a date. To have the date display as a quarter use a quarter format to display the date.
date_qtr_boundary = intnx('quarter', today(), 0, 'e') + 8;
Then you can compare your dates to the boundary value rather than the number 8. I'm having a hard time following exactly what you want to determine, but if you post some sample data and expected output, I (or someone else) can provide more details.

Related

SAS macro to get the last date of the current month

I would like to use a macro in SAS to calculate the last day of the current month when executed.
As i'm quite new to the SAS macro's i've tried to create on based on the information i've found on the internet.
%let last_day = %sysfunc(putn(%sysfunc(intnx(month,%sysfunc(today()),e), date9.));
However it does not seem to work when i execute it.
You left out the number of intervals in the INTNX() function call.
To create a macro variable with the string that looks like the last day of the current month in the style produced by the DATE9. format just use:
%let last_day = %sysfunc(intnx(month,%sysfunc(today()),0,e), date9.);
You could then use that macro variable to generate strings. Such as in a TITLE statement.
TITLE "End of the month is &last_day";
If you want to use it as an actual date you will need to convert it to a date literal by adding quotes and the letter d.
...
where date <= "&last_day"d ;
And if so it is probably simpler to not use the DATE9. format at all and just store the raw number of days since 1960 in the macro variable.
%let last_day = %sysfunc(intnx(month,%sysfunc(today()),0,e));
...
where date <= &last_day ;

write conditional in SAS with DATA _NULL_

I am writing a conditional in SAS starts with DATA NULL
%LET today = today();
DATA _NULL_;
if day(today) ge 1 and day(today) le 15 then do;
date1=put(intnx('month',today,-1,'E'), date11.);
date2=put(intnx('month',today,-1,'L'), date11.);
end;
if day(today) > 15 then do;
date1=put(intnx('month',today,0,'B'), date11.);
date2=put(intnx('month',today,0,'L'), date11.);
end;
call symput('report_date',date1);
call symput('report_date2',date2);
RUN;
but with above, I am not getting any values for my report_dates.
the condition is:
date 1 = If the current date is greater than or equal to 1 and less than 16, set the date1 to the 16th of the previous month, otherwise set it to the 1st of the current month
date2 = If the current date is 16 and above, set the date2 to the 15th of the current month, otherwise set date2 to the last day of the previous month
The IF/THEN logic does not account for the missing value you passing to the DAY() function calls.
The variable TODAY is never created in the data step. So just remove the %LET statement and add an actual assignment statement instead.
DATA _NULL_;
today=today();
if day(today) ge 1 and day(today) le 15 then do;
...
Just because you used the same name for the macro variable in the %LET statement as you used for the variable in the data step does not imply that the two have anything at all to do with each other.
If you wanted to use the macro variable to generate the code for the data step you would need to replace the TODAY with &TODAY.
if day(&today) ge 1 and day(&today) le 15 then do;
So for the value you set to the macro variable TODAY it would mean that the SAS code you are trying to run is:
if day(today()) ge 1 and day(today()) le 15 then do;
Note that is actually not a good way to handle this problem because it is calling the TODAY() function multiple times. That could cause strange results if the data step started right before midnight. So the IF condition might be run on the 31st but when you get to the ELSE condition the clock has ticked over to the first of the next month.

Calculate average of the last x years

I have the following data
Date value_idx
2002-01-31 .
2002-01-31 24.533
2002-01-31 26.50
2018-02-28 25.2124
2019-09-12 22.251
2019-01-31 24.214
2019-05-21 25.241
2019-05-21 .
2020-05-21 25.241
2020-05-21 23.232
I would need to calculate the average of value_idx of the last 3 years and 7 years.
I tried first to calculate it as follows:
proc sql;
create table table1 as
select date, avg(value_idx) as avg_value_idx
from table
group by date;
quit;
The problem is that I do not know how to calculate the average of value_idx not per each month but for the last two years. So I think I should extract the year, group by that, and then calculate the average.
I hope someone of you can help me with this.
You can use CASE to decide which records contribute to which MEAN. You need to clarify what you mean by last 2 or last 7 years. This code will find the value of the maximum date and then compare the year of that date to the year of the other dates.
select
mean(case when year(max_date)-year(date) < 2 then value_idx else . end) as mean_yr2
,mean(case when year(max_date)-year(date) < 7 then value_idx else . end) as mean_yr7
from have,(select max(date) as max_date from have)
;
Results
mean_yr2 mean_yr7
------------------
24.0358 24.2319
The best way to do this sort of thing in SAS is with native PROCs, as they have a lot of functionality related to grouping.
In this case, we use multilabel formats to control the grouping. I assume you mean 'Last Three Years' as in calendar 2018/2019/2020 and 'Last Seven Years' as calendar 2014-2020. Presumably you can see how to modify this for other time periods - so long as you aren't trying to make the time period relative to each data point.
We create a format that uses the MULTILABEL option (which allows data points to fall in multiple categories), and the NOTSORTED option (to allow us to force the ordering of the labels, otherwise SEVEN is earlier than THREE).
Then, we use it in PROC TABULATE, enabling it with MLF (MultiLabel Format) and preloadfmt order=data which again keeps the ordering correct. This produces a report with the two averages only.
data have;
informat date yymmdd10.;
input Date value_idx;
datalines;
2002-01-31 .
2002-01-31 24.533
2002-01-31 26.50
2017-02-28 25.2124
2017-09-12 22.251
2018-01-31 24.214
2018-05-21 25.241
2019-05-21 .
2020-05-21 25.241
2020-05-21 23.232
;;;;
run;
proc format;
value yeartabfmt (multilabel notsorted)
'01JAN2018'd-'31DEC2020'd = 'Last Three Years'
'01JAN2014'd-'31DEC2020'd = 'Last Seven Years'
other=' '
;
quit;
proc tabulate data=have;
class date/mlf preloadfmt order=data;
var value_idx;
format date yeartabfmt.;
tables date,value_idx*mean;
run;

Dates and intervals in SAS

If I have two dates e.g. 22MAR2005 to 01MAR2006 and I want to create season intervals (spring, summer, autumn, winter) based on this interval, how can this be done in a data step?
Season's are defined as:
Spring: March to May
Summer: June to August
Autumn: September to November
Winter: December to February
I need to calculate how long they spent in each season.
You need to convert that to MONTH first and then to SEASON. This exact question was asked recently so it's relatively easy to find via search (I'm assuming some course is using this as homework?).
data want;
set have;
*create month;
month_date = month(date);
*assign to season;
if month_date in (6, 7, 8) then season = 'Summer';
else if month_date in (9, 10, 11) then season ='Fall';
....etc;
run;
You could also use a format but since you're just starting out this is likely easier.
Other users question which seems really really similar:
https://communities.sas.com/t5/SAS-Enterprise-Guide/proc-glm/m-p/492142
EDIT: Use INTCK() to calculate the number of intervals.
Then use INTNX to increment across the intervals and count your days.
How you align your dates can be controlled with the first parameter to the INTNX() function. You can check the documentation for the exact specifics.
data want;
start_date="22Mar2005"d;
end_date="01Mar2006"d;
num_intervals=intck('quarter', start_date, end_date, 'C') ;
do interval=0 to num_intervals;
season_start=intnx('Month3.3', start_date, interval, 'b');
season_end=intnx('Month3.3', start_date, interval, 'e');
Number_Days=season_end - season_start + 1;
output;
end;
format start_date end_date season: yymmddd10.;
run;

SAS function to remove data if the month matches between 2 dates in 2 set of columns

I have a large data with dates on 2 columns (15 years data). I am looking to remove the date if the month between the 2 columns match (dont care about the day or the year) EX: 01FEB2006 - 01FEB2017 now i need to remove this date as the month matches while 16FEB2006 - 23JUN2017 doesn't therefore i need a new column to say the difference is 4 months. If the difference is +/- 30 days between the 2 months, this have to be removed Ex: 01jan2005 - 20dec2014.
Please help! I am still learning SAS.
Thanks in advance for your help.
Okay. The question is a bit unclear. But as I've deduced the question has number of potential questions:
"Delete obeservations that have matching months"
This may be obtained using month function. (Or if your date is string then substring.) http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000197966.htm
Data refined;
set begin;
month1=month(date_var1 );
month2=month(date_var2 );
if month1= month2 then delete;
run;
"I need a new column to say the difference is 4 months."
intck if your friendly function here. You can calculate the timedifferences between the dates. http://www.sascommunity.org/wiki/INTCK_function_examples
Data refined;
set begin;
TimeDifference = intck('month', date_var1, date_var2);
run;
"if datedifference is +-30 days then delete the observation"
Same as above, but with days in this case.
Data refined;
set begin;
TimeDifference = intck('days', date_var1, date_var2) ;
if abs(TimeDifference) <=30 delete;
run;