pandas-dev / pandas

Flexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more
https://pandas.pydata.org
BSD 3-Clause "New" or "Revised" License
43.52k stars 17.88k forks source link

ENH: Add support for generating 4-4-5 but by month not just quarter or year #59977

Open KuriaMaingi opened 1 week ago

KuriaMaingi commented 1 week ago

Feature Type

Problem Description

Currently, you can generate quarters that fit the 4-4-5 fiscal calendar as is common in the retail world. https://pandas.pydata.org/docs/reference/api/pandas.tseries.offsets.FY5253Quarter.html

How can I generate a datetime index by month (generating a "future" dataframe for forecasting purposes) that matches the input data which is monthly & follows that 4-4-5 calendar?

For example, input data covers these time periods: ['2020-07-05 00:00:00', '2020-08-02 00:00:00', '2020-08-30 00:00:00', '2020-10-04 00:00:00', '2020-11-01 00:00:00', '2020-11-29 00:00:00', '2021-01-03 00:00:00', '2021-01-31 00:00:00', '2021-02-28 00:00:00', '2021-04-04 00:00:00', '2021-05-02 00:00:00', '2021-05-30 00:00:00', '2021-07-04 00:00:00', '2021-08-01 00:00:00', '2021-08-29 00:00:00', '2021-10-03 00:00:00', '2021-10-31 00:00:00', '2021-11-28 00:00:00', '2022-01-02 00:00:00', '2022-01-30 00:00:00', '2022-02-27 00:00:00', '2022-04-03 00:00:00', '2022-05-01 00:00:00', '2022-05-29 00:00:00', '2022-07-03 00:00:00', '2022-07-31 00:00:00', '2022-08-28 00:00:00', '2022-10-02 00:00:00', '2022-10-30 00:00:00', '2022-11-27 00:00:00', '2023-01-01 00:00:00']

I can generate the quarters that match up with this frequency by using the following code: yoffset = pd.offsets.FY5253Quarter(weekday=6, startingMonth=8, variation="last") yindex = pd.date_range('2020-08-30', periods=12, freq=yoffset) yindex

which in turn generates the correct output: DatetimeIndex(['2020-08-30', '2020-11-29', '2021-02-28', '2021-05-30', '2021-08-29', '2021-11-28', '2022-02-27', '2022-05-29', '2022-08-28', '2022-11-27', '2023-02-26', '2023-05-28'], dtype='datetime64[ns]', freq='REQ-L-AUG-SUN-1')

How can I resample these quarterly periods to generate an equivalent 4-4-5 monthly frequency that follows the same ruleset?

Feature Description

pd.offsets.FY5253Month(weekday=x, startingMonth=y, variation="z)

Alternative Solutions

Not aware of any and currently stuck

Additional Context

No response

Liam3851 commented 5 days ago

As an alternative solution, if you have a date range and want to map it to month on a 4-4-5, you could do something like this, which finds the prior qend, determines the 4-4-5 week, and then maps that to a 4-4-5 month:

dr = pd.date_range(yindex[0], yindex[-1])
prior_qend = dr.map(yoffset.rollback)
week_445 = (dr - prior_qend).days // 7
month_445 = np.where(week_445 < 4, 0, np.where(week_445 < 8, 1, 2))
monthend_445 = pd.Series(prior_qend + month_445 * pd.Timedelta(28, 'D'), dr)

That said, perhaps 4-4-5 month and week might be sensible to include as I imagine the calcs are already being made to implement FY5253Quarter.

KuriaMaingi commented 4 days ago

As an alternative solution, if you have a date range and want to map it to month on a 4-4-5, you could do something like this, which finds the prior qend, determines the 4-4-5 week, and then maps that to a 4-4-5 month:

dr = pd.date_range(yindex[0], yindex[-1])
prior_qend = dr.map(yoffset.rollback)
week_445 = (dr - prior_qend).days // 7
month_445 = np.where(week_445 < 4, 0, np.where(week_445 < 8, 1, 2))
monthend_445 = pd.Series(prior_qend + month_445 * pd.Timedelta(28, 'D'), dr)

That said, perhaps 4-4-5 month and week might be sensible to include as I imagine the calcs are already being made to implement FY5253Quarter.

Yes this is a good workaround and I'll give it a try. The only issue is if I need the time period offsets as a Pandas dateoffset function for use in other libraries like Nixtla, it's a lot harder to integrate when it is a custom function like this.

It's doable but ideally, especially if the underlying calcs may exist, to have this as an explicitly defined offset