Been spinning my wheels on this so posting here because I know I'm overthinking it and coming up with nothing.
In my program I have an imported schedule. The schedule may vary from "2,4,6" or "2,4" or even '"4" where those are days of the week. The schedule will be dynamic and will be controlled outside the program; it's imported. What is consistent is that the schedule will pull data since the last scheduled run so for example:
if the schedule is "2,4,6", the program will go back "3,2,2" days respectively. I know that the formula for this is (today's weekday minus the most recent run day plus 7) then mod 7. What I'm not sure about is an efficient way (or any) to get the left value when I'm talking about the first value. I figured I could potentially use a coalesce (value to left, but otherwise the last value in list), but then I think I'm over-complicating this.
I want this to be dynamic since the schedules won't be static. Any advice?
What's the most recent run day? Where is that defined?
Otherwise this seems pretty simple but it depends on how your data exists.
array run_dates(*) run_date1-run_date3 (2, 4, 6);
recentRunDayofWeek = run_dates(dim(run_dates));
parm1 = mod(run_dates(1)- recentRunDayofWeek +7, 7);
parm2 = mod(run_dates(2)-run_dates(1) + 7, 7);
parm3 = mod(run_dates(3)-run_dates(2) +7, 7);
Modified based on responses so far. If this does work, you can generalize it to a loop once you've determine how you're passing information back and forth.
I have no idea what you are trying to describe.
What is it that you are trying to calculate? It sort of sounds like you want to find the most recent day relative to today that the process was scheduled to run? If so do you want to include today?
Provide concrete examples. Show how the data you have coming in and what result you want. Make sure to post the example as a simple data step to create the data so it is easy to use and clearly shows the names and types of your variables.
The run schedule will be dynamic. As an example, one internal customer may want the report run on weekdays 2,4,6 or Monday, Wednesday, and Friday. Each day it's ran it will go back to the last time it was ran to pull data in. Today being Monday, there is no value to the left to subtract exactly, but in that case we know that the last day it was run would be the last day of the array so in that case that's 6 or Friday so I know that's 3 days or as I was saying "2,4,6" becomes "3,2,2". On Monday go back 3 days, on Wednesday, go back 2, and on Friday also go back 2 days. Again, this is just an example and schedules could vary and it might be once a week or twice a week.
I figured I could use the new particular matching value from "3,2,2" array which could be called "Previous_dates_to_include" to subtract from today's date when today's weekday is in the original schedule array.
There will be potentially a couple dozen different schedules so this will all be "macrotized" and I was considering potentially converting it at the same time I convert the Excel spreadsheet to macro variables.
Thanks for getting back so quickly with your thoughts and additional questions!
Still not following this completely, but it looks like there is a problem with your plan. If you want to find the last time it ran you should keep track of when it ran, not depend on when it was supposed to run. Things happen.
Are you asking how to implement such a planning system?
If you just want make a way to represent which days of the week a process is schedule to run you really don't need the commas. Just use a 7 character string. You could store the digits 1 to 7. So '246' or '23'. or always have 7 characters and just use 0/1 (or any other pair of symbols) to mark if that day of the week is included. So '0101010' or '0110000'.
Are you asking how to implement modulo indexing?
today=1;
yesterday=today-1;
if yesterday<1 then yesterday=7;
I think you are saying that you are given a weekly schedule, showing specific days of the week for a given event. And you want to calculate the number of days between each event and the most recent event, correct? And the first event for a given week follows the last event for the prior week, which had the same schedule as the current week, yes?
Then:
data schedules;
input sch1 sch2 sch3;
datalines;
2 4 6 /* M W F*/
1 3 5 /* Su Tu Th */
1 3 6 /* Su W Sa */
run;
data want (drop=s);
set schedules;
array _sch {*} sch1-sch3;
array _days {*} days1-days3;
do s=1 to dim(_sch);
if s=1 then _days{s}= _sch{1}+7 - _sch{dim(_sch)} ;
else _days{s}=_sch{s}-_sch{s-1};
end;
run;
By using the "if s=1" statement, you don't really need to do mod7 arithmetic.
Editted supplement: But if you really want to do MOD arithmetic, you can do it both on the array declaration (mod 3) and the result (mod 7):
data want2 (drop=s);
set schedules;
array _sch {*} sch1-sch3;
array _days {*} days1-days3;
do s=1 to dim(_days);
_days{s}= mod(_sch{s}+7 - _sch{mod(s+1,3)+1},7);
end;
run;
In the case of the MOD7 component, you don't need to worry about generating a 7-day interval (which would be zero mod 7), since we assume three dates per week, with ascending values. But you do need to do an adjustment for the MOD3 component - this avoids trying to subtract _SCH{1} minus _SCH{0} - which doesn't exist.
But even that can be avoided by changing the definition of the _SCH array. This is probably the simplest code that avoid using an IF test:
data want3 (drop=s);
set schedules;
array _sch{0:3} sch3 sch1-sch3;
array _days {*} days1-days3;
do s=1 to dim(_days);
_days{s}= mod(_sch{s}+7 - _sch{s-1},7);
end;
run;
The "trick" here is to define the _SCH array to have 4 elements, starting not at element 1, but at element 0 (which has the same value as element 3). So when the array index S=1 you will get _days{1} = _sch{1}+7 - _sch{0} modulo 7.
This is the problem when you don't completely explain the task, as @Tom requested. By "dynamic" I now understand you to mean not only a variation in days of week, but also of number of events per week. Please provide an actual sample data set in the form of a data step, and an example of what the resulting data set would look like.
Help us help you.
Supplement: In the meantime, here is code that accepts whatever number of events per week you encounter (programmed to accept up to 5 here):
data schedules;
infile datalines missover;
input sch1 sch2 sch3 sch4 sch5 ;
datalines;
2 4 6
1 3 5
1 3 6
1 2 3 4 6
2
run;
data want (drop=_:);
set schedules;
_last_sch=max(of sch:);
array _sch{*} _last_sch sch: ;
array _days {*} days1-days5;
if n(of sch:)=1 then days1=7;
else do _s=1 to dim(_days) while (not missing(_sch{_s+1}));
_days{_s}= mod(_sch{_s+1}+7 - _sch{_s},7);
end;
run;
Except for 1 day a week, this assumes that the first and last days of each week are different.
@mikeydubs23 wrote:
Thanks mkeintz, this is on the right path. It works for the 3 day schedules, but I'll have to adjust to make it a little more dynamic (maybe counting the array first) to accommodate for schedules where it only runs once or twice or potentially more although that's not really a possibility currently.
Your ARRAY should have a dimension of 7. And to find the last item don't use DIM() using N() instead to count the non-missing values.
Again thanks for all the help. Her's what I settled on using your suggestions
I had to switch the input to space delimited since I couldn't get commas to work, but the config file actually has many more elements so I created a single column dataset to show how it works. One other thing I left out was that I was originally trying to do this with these values stored in macro variables and I would have set the days variable to a macro variable too, but datasets should work just as well.
data schedule;
infile cards ;
input Schedule $13.;
cards;
2 4 6
2 4
4
2 5
run;
data Schedule_Import (drop=_: i);
set Schedule (rename=(Schedule=_schedule));
array schedule(7);
i=1;
do until (scan(_schedule,i," ") eq "");
schedule(i)=scan(_schedule,i," ");
i+1;
end;
run;
data Schedule_Export (drop=_:);
set Schedule_Import;
_last_schedule=max(of schedule:);
schedule_count=n(of schedule:);
array _schedule{*} schedule: ;
put array _schedule{*} ;
array _days {*} days1-days7;
if schedule_count=1 then days1=7;
else do _s=1 to schedule_count;
if _s=1 then _days{_s} = mod(_schedule{_s} -_last_schedule +7 ,7);
else _days{_s} = _schedule{_s} - _schedule{_s-1};
end;
put exit;
run;
It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.