Search code examples
sassas-macro

SAS - rolling N months


I am looking to use a factor based on YRMON (YYYY:MM) from a static list of factors that are based on the last 12 months (rolling forward every month)

I have a static dataset that looks this simple (see below): I basically need to create a format that changes the yrmon to a rolling 13 months that I can stamp on the factor (for calculation needs) based on the yrmon.

yrmon   facto
DATE00  1.00000 
DATE01  0.99944 
DATE02  0.99907 
DATE03  0.99907 
DATE04  0.99889 
DATE05  0.99799 
DATE06  0.99659 
DATE07  0.99500 
DATE08  0.99296 
DATE09  0.99100 
DATE10  0.85000 
DATE11  0.78000 
DATE12  0.34900 

Example Data before/after:

YRMON     PAYAMT         PAYAMT_AFTER
2020:10   $100.00        $34.90
2020:08   $100.00        $85.00

Here's what I have so far:

/*Create macro dates*/
%let to_date = %sysfunc(date());
%let paydt = %sysfunc(intnx(month,&to_date, -(0)));
%let payday = %eval(&paydt. -1);


%let chirodt12 = %sysfunc(intnx(month,&payday,-0), date9.);
%let chirodt11 = %sysfunc(intnx(month,&payday,-1), date9.);
%let chirodt10 = %sysfunc(intnx(month,&payday,-2), date9.);
%let chirodt9 = %sysfunc(intnx(month,&payday,-3), date9.);
%let chirodt8 = %sysfunc(intnx(month,&payday,-4), date9.);
%let chirodt7 = %sysfunc(intnx(month,&payday,-5), date9.);
%let chirodt6 = %sysfunc(intnx(month,&payday,-6), date9.);
%let chirodt5 = %sysfunc(intnx(month,&payday,-7), date9.);
%let chirodt4 = %sysfunc(intnx(month,&payday,-8), date9.);
%let chirodt3 = %sysfunc(intnx(month,&payday,-9), date9.);
%let chirodt2 = %sysfunc(intnx(month,&payday,-10), date9.);
%let chirodt1 = %sysfunc(intnx(month,&payday,-11), date9.);
%let chirodt0 = %sysfunc(intnx(month,&payday,-12), date9.);

%put  chirodt12 &chirodt12.;
%put  chirodt11 &chirodt11.;
%put  chirodt10 &chirodt10.;
%put  chirodt9 &chirodt9.;
%put  chirodt8 &chirodt8.;
%put  chirodt7 &chirodt7.;
%put  chirodt6 &chirodt6.;
%put  chirodt5 &chirodt5.;
%put  chirodt4 &chirodt4.;
%put  chirodt3 &chirodt3.;
%put  chirodt2 &chirodt2.;
%put  chirodt1 &chirodt1.;
%put  chirodt0 &chirodt0.;

filename ibnr "/path/to/file/file.txt";
data ibnr;
infile ibnr dlm='09'X dsd missover firstobs = 2;
informat month $6. factor 7.5;
input month factor;
run;

data ibnr;
    set ibnr;
    if month = 'DATE12' then yrmon = put("&chirodt12"D, yymmc7.);
    else if month = 'DATE11' then yrmon = put("&chirodt11"D, yymmc7.);
    else if month = 'DATE10' then yrmon = put("&chirodt10"D, yymmc7.);
    else if month = 'DATE09' then yrmon = put("&chirodt9"D, yymmc7.);
    else if month = 'DATE08' then yrmon = put("&chirodt8"D, yymmc7.);
    else if month = 'DATE07' then yrmon = put("&chirodt7"D, yymmc7.);
    else if month = 'DATE06' then yrmon = put("&chirodt6"D, yymmc7.);
    else if month = 'DATE05' then yrmon = put("&chirodt5"D, yymmc7.);
    else if month = 'DATE04' then yrmon = put("&chirodt4"D, yymmc7.);
    else if month = 'DATE03' then yrmon = put("&chirodt3"D, yymmc7.);
    else if month = 'DATE02' then yrmon = put("&chirodt2"D, yymmc7.);
    else if month = 'DATE01' then yrmon = put("&chirodt1"D, yymmc7.);
    else if month = 'DATE00' then yrmon = put("&chirodt0"D, yymmc7.);
run;

/*format for later use to stamp on factor for calculations*/
data chrofact (keep = fmtname label start type hlo);
retain fmtname 'chfact' type 'C';
set ibnr end = lastrec;
start=yrmon;
label = factor;
output;
if lastrec then do;
start='XXX' ;
hlo = 'O';
label = '0';
output;
end;
run;

proc format cntlin = chrofact;
run;

This gives me what I want; but I'm just sure there's a simpler method out there


Solution

  • Converting a text value to a SAS date value is an INPUT operation (i.e. INFORMAT), not a PUT operation (i.e. FORMAT).

    Instead consider creating a lookup table that maps a literal string "DATE<nn>" to a SAS date value corresponding to some predetermined base date.

    Example

    Decode the <nn> portion of the string literal and apply it in a call to INTNX. Use the output as the basis of CNTLIN for INFORMAT or as a lookup table LEFT JOINED or HASH JOINED to some other data set containing string literals "DATE"

    data have;
    length yrmon $6 facto 8.;
    input yrmon   facto;
    basedate = today();
    month = input(substr(yrmon,5),2.);
    month_ago = 12-month;
    ago_date = intnx('month', basedate, -month_ago);
    format ago_date yymmdd10.;
    datalines;
    DATE00  1.00000 
    DATE01  0.99944 
    DATE02  0.99907 
    DATE03  0.99907 
    DATE04  0.99889 
    DATE05  0.99799 
    DATE06  0.99659 
    DATE07  0.99500 
    DATE08  0.99296 
    DATE09  0.99100 
    DATE10  0.85000 
    DATE11  0.78000 
    DATE12  0.34900 
    ;
    

    Image of output data set in viewer

    enter image description here