Skip to content

Custom Business Month #6866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 22, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions doc/source/timeseries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@ frequency increment. Specific offset logic like "month", "business day", or
MonthBegin, "calendar month begin"
BMonthEnd, "business month end"
BMonthBegin, "business month begin"
CBMonthEnd, "custom business month end"
CBMonthBegin, "custom business month begin"
QuarterEnd, "calendar quarter end"
QuarterBegin, "calendar quarter begin"
BQuarterEnd, "business quarter end"
Expand Down Expand Up @@ -558,6 +560,20 @@ As of v0.14 holiday calendars can be used to provide the list of holidays. See
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
dt + bday_us

Monthly offsets that respect a certain holiday calendar can be defined
in the usual way.

.. ipython:: python

from pandas.tseries.offsets import CustomBusinessMonthBegin
bmth_us = CustomBusinessMonthBegin(calendar=USFederalHolidayCalendar())
# Skip new years
dt = datetime(2013, 12, 17)
dt + bmth_us

# Define date index with custom offset
from pandas import DatetimeIndex
DatetimeIndex(start='20100101',end='20120101',freq=bmth_us)

.. note::

Expand Down Expand Up @@ -601,8 +617,10 @@ frequencies. We will refer to these aliases as *offset aliases*
"W", "weekly frequency"
"M", "month end frequency"
"BM", "business month end frequency"
"CBM", "custom business month end frequency"
"MS", "month start frequency"
"BMS", "business month start frequency"
"CBMS", "custom business month start frequency"
"Q", "quarter end frequency"
"BQ", "business quarter endfrequency"
"QS", "quarter start frequency"
Expand Down
1 change: 1 addition & 0 deletions doc/source/v0.14.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ Enhancements
- Implemented ``Panel.pct_change`` (:issue:`6904`)
- Added ``how`` option to rolling-moment functions to dictate how to handle resampling; :func:``rolling_max`` defaults to max,
:func:``rolling_min`` defaults to min, and all others default to mean (:issue:`6297`)
- ``CustomBuisnessMonthBegin`` and ``CustomBusinessMonthEnd`` are now available (:issue:`6866`)

Performance
~~~~~~~~~~~
Expand Down
8 changes: 7 additions & 1 deletion pandas/core/datetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@
try:
cday = CDay()
customBusinessDay = CustomBusinessDay()
customBusinessMonthEnd = CBMonthEnd()
customBusinessMonthBegin = CBMonthBegin()
except NotImplementedError:
cday = None
customBusinessDay = None
customBusinessMonthEnd = None
customBusinessMonthBegin = None
monthEnd = MonthEnd()
yearEnd = YearEnd()
yearBegin = YearBegin()
bmonthEnd = BMonthEnd()
businessMonthEnd = bmonthEnd
bmonthBegin = BMonthBegin()
cbmonthEnd = customBusinessMonthEnd
cbmonthBegin = customBusinessMonthBegin
bquarterEnd = BQuarterEnd()
quarterEnd = QuarterEnd()
byearEnd = BYearEnd()
Expand Down
176 changes: 154 additions & 22 deletions pandas/tseries/offsets.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sys
from datetime import date, datetime, timedelta
from pandas.compat import range
from pandas import compat
Expand All @@ -16,6 +17,7 @@
import functools

__all__ = ['Day', 'BusinessDay', 'BDay', 'CustomBusinessDay', 'CDay',
'CBMonthEnd','CBMonthBegin',
'MonthBegin', 'BMonthBegin', 'MonthEnd', 'BMonthEnd',
'YearBegin', 'BYearBegin', 'YearEnd', 'BYearEnd',
'QuarterBegin', 'BQuarterBegin', 'QuarterEnd', 'BQuarterEnd',
Expand Down Expand Up @@ -703,6 +705,132 @@ def onOffset(cls, dt):
_prefix = 'BMS'



class CustomBusinessMonthEnd(MonthOffset):
"""
**EXPERIMENTAL** DateOffset of one custom business month

.. warning:: EXPERIMENTAL

This class is not officially supported and the API is likely to change
in future versions. Use this at your own risk.

Parameters
----------
n : int, default 1
offset : timedelta, default timedelta(0)
normalize : bool, default False
Normalize start/end dates to midnight before generating date range
weekmask : str, Default 'Mon Tue Wed Thu Fri'
weekmask of valid business days, passed to ``numpy.busdaycalendar``
holidays : list
list/array of dates to exclude from the set of valid business days,
passed to ``numpy.busdaycalendar``
"""

_cacheable = False
_prefix = 'CBM'
def __init__(self, n=1, **kwds):
self.n = int(n)
self.kwds = kwds
self.offset = kwds.get('offset', timedelta(0))
self.normalize = kwds.get('normalize', False)
self.weekmask = kwds.get('weekmask', 'Mon Tue Wed Thu Fri')
holidays = kwds.get('holidays', [])
self.cbday = CustomBusinessDay(n=self.n,**kwds)
self.m_offset = MonthEnd()

@apply_nat
def apply(self,other):
n = self.n
dt_in = other
# First move to month offset
cur_mend = self.m_offset.rollforward(dt_in)
# Find this custom month offset
cur_cmend = self.cbday.rollback(cur_mend)

# handle zero case. arbitrarily rollforward
if n == 0 and dt_in != cur_cmend:
n += 1

if dt_in < cur_cmend and n >= 1:
n -= 1
elif dt_in > cur_cmend and n <= -1:
n += 1

new = cur_mend + n * MonthEnd()
result = self.cbday.rollback(new)
return as_timestamp(result)

def __repr__(self):
if sys.version_info.major < 3:
return BusinessDay.__repr__.__func__(self)
else:
return BusinessDay.__repr__(self)

class CustomBusinessMonthBegin(MonthOffset):
"""
**EXPERIMENTAL** DateOffset of one custom business month

.. warning:: EXPERIMENTAL

This class is not officially supported and the API is likely to change
in future versions. Use this at your own risk.

Parameters
----------
n : int, default 1
offset : timedelta, default timedelta(0)
normalize : bool, default False
Normalize start/end dates to midnight before generating date range
weekmask : str, Default 'Mon Tue Wed Thu Fri'
weekmask of valid business days, passed to ``numpy.busdaycalendar``
holidays : list
list/array of dates to exclude from the set of valid business days,
passed to ``numpy.busdaycalendar``
"""

_cacheable = False
_prefix = 'CBMS'
def __init__(self, n=1, **kwds):
self.n = int(n)
self.kwds = kwds
self.offset = kwds.get('offset', timedelta(0))
self.normalize = kwds.get('normalize', False)
self.weekmask = kwds.get('weekmask', 'Mon Tue Wed Thu Fri')
holidays = kwds.get('holidays', [])
self.cbday = CustomBusinessDay(n=self.n,**kwds)
self.m_offset = MonthBegin()

@apply_nat
def apply(self,other):
n = self.n
dt_in = other
# First move to month offset
cur_mbegin = self.m_offset.rollback(dt_in)
# Find this custom month offset
cur_cmbegin = self.cbday.rollforward(cur_mbegin)

# handle zero case. arbitrarily rollforward
if n == 0 and dt_in != cur_cmbegin:
n += 1

if dt_in > cur_cmbegin and n <= -1:
n += 1
elif dt_in < cur_cmbegin and n >= 1:
n -= 1

new = cur_mbegin + n * MonthBegin()
result = self.cbday.rollforward(new)
return as_timestamp(result)


def __repr__(self):
if sys.version_info.major < 3:
return BusinessDay.__repr__.__func__(self)
else:
return BusinessDay.__repr__(self)

class Week(DateOffset):
"""
Weekly offset
Expand Down Expand Up @@ -1906,6 +2034,8 @@ class Nano(Tick):
BDay = BusinessDay
BMonthEnd = BusinessMonthEnd
BMonthBegin = BusinessMonthBegin
CBMonthEnd = CustomBusinessMonthEnd
CBMonthBegin = CustomBusinessMonthBegin
CDay = CustomBusinessDay


Expand Down Expand Up @@ -1988,28 +2118,30 @@ def generate_range(start=None, end=None, periods=None,
cur = next_date

prefix_mapping = dict((offset._prefix, offset) for offset in [
YearBegin, # 'AS'
YearEnd, # 'A'
BYearBegin, # 'BAS'
BYearEnd, # 'BA'
BusinessDay, # 'B'
BusinessMonthBegin, # 'BMS'
BusinessMonthEnd, # 'BM'
BQuarterEnd, # 'BQ'
BQuarterBegin, # 'BQS'
CustomBusinessDay, # 'C'
MonthEnd, # 'M'
MonthBegin, # 'MS'
Week, # 'W'
Second, # 'S'
Minute, # 'T'
Micro, # 'U'
QuarterEnd, # 'Q'
QuarterBegin, # 'QS'
Milli, # 'L'
Hour, # 'H'
Day, # 'D'
WeekOfMonth, # 'WOM'
YearBegin, # 'AS'
YearEnd, # 'A'
BYearBegin, # 'BAS'
BYearEnd, # 'BA'
BusinessDay, # 'B'
BusinessMonthBegin, # 'BMS'
BusinessMonthEnd, # 'BM'
BQuarterEnd, # 'BQ'
BQuarterBegin, # 'BQS'
CustomBusinessDay, # 'C'
CustomBusinessMonthEnd, # 'CBM'
CustomBusinessMonthBegin, # 'CBMS'
MonthEnd, # 'M'
MonthBegin, # 'MS'
Week, # 'W'
Second, # 'S'
Minute, # 'T'
Micro, # 'U'
QuarterEnd, # 'Q'
QuarterBegin, # 'QS'
Milli, # 'L'
Hour, # 'H'
Day, # 'D'
WeekOfMonth, # 'WOM'
FY5253,
FY5253Quarter,
])
Expand Down
Loading