Skip to content

Commit

Permalink
DEPR: rename ‘BM’/‘CBM’ to ‘BME’/‘CBME’ for offsets (#55496)
Browse files Browse the repository at this point in the history
* rename BM, CBM to BME, CBME for offsets

* fix tests

* correct docs

* add tests, add notes to 2.2.0 whatsnew
  • Loading branch information
natmokval authored Oct 14, 2023
1 parent b5b8be0 commit 0021d24
Show file tree
Hide file tree
Showing 17 changed files with 68 additions and 37 deletions.
16 changes: 8 additions & 8 deletions doc/source/user_guide/timeseries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ of those specified will not be generated:

.. ipython:: python
pd.date_range(start, end, freq="BM")
pd.date_range(start, end, freq="BME")
pd.date_range(start, end, freq="W")
Expand Down Expand Up @@ -557,7 +557,7 @@ intelligent functionality like selection, slicing, etc.

.. ipython:: python
rng = pd.date_range(start, end, freq="BM")
rng = pd.date_range(start, end, freq="BME")
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts.index
ts[:5].index
Expand Down Expand Up @@ -884,9 +884,9 @@ into ``freq`` keyword arguments. The available date offsets and associated frequ
:class:`~pandas.tseries.offsets.LastWeekOfMonth`, ``'LWOM'``, "the x-th day of the last week of each month"
:class:`~pandas.tseries.offsets.MonthEnd`, ``'ME'``, "calendar month end"
:class:`~pandas.tseries.offsets.MonthBegin`, ``'MS'``, "calendar month begin"
:class:`~pandas.tseries.offsets.BMonthEnd` or :class:`~pandas.tseries.offsets.BusinessMonthEnd`, ``'BM'``, "business month end"
:class:`~pandas.tseries.offsets.BMonthEnd` or :class:`~pandas.tseries.offsets.BusinessMonthEnd`, ``'BME'``, "business month end"
:class:`~pandas.tseries.offsets.BMonthBegin` or :class:`~pandas.tseries.offsets.BusinessMonthBegin`, ``'BMS'``, "business month begin"
:class:`~pandas.tseries.offsets.CBMonthEnd` or :class:`~pandas.tseries.offsets.CustomBusinessMonthEnd`, ``'CBM'``, "custom business month end"
:class:`~pandas.tseries.offsets.CBMonthEnd` or :class:`~pandas.tseries.offsets.CustomBusinessMonthEnd`, ``'CBME'``, "custom business month end"
:class:`~pandas.tseries.offsets.CBMonthBegin` or :class:`~pandas.tseries.offsets.CustomBusinessMonthBegin`, ``'CBMS'``, "custom business month begin"
:class:`~pandas.tseries.offsets.SemiMonthEnd`, ``'SM'``, "15th (or other day_of_month) and calendar month end"
:class:`~pandas.tseries.offsets.SemiMonthBegin`, ``'SMS'``, "15th (or other day_of_month) and calendar month begin"
Expand Down Expand Up @@ -1248,8 +1248,8 @@ frequencies. We will refer to these aliases as *offset aliases*.
"W", "weekly frequency"
"ME", "month end frequency"
"SM", "semi-month end frequency (15th and end of month)"
"BM", "business month end frequency"
"CBM", "custom business month end frequency"
"BME", "business month end frequency"
"CBME", "custom business month end frequency"
"MS", "month start frequency"
"SMS", "semi-month start frequency (1st and 15th)"
"BMS", "business month start frequency"
Expand Down Expand Up @@ -1586,7 +1586,7 @@ rather than changing the alignment of the data and the index:
ts.shift(5, freq="D")
ts.shift(5, freq=pd.offsets.BDay())
ts.shift(5, freq="BM")
ts.shift(5, freq="BME")
Note that with when ``freq`` is specified, the leading entry is no longer NaN
because the data is not being realigned.
Expand Down Expand Up @@ -1692,7 +1692,7 @@ the end of the interval.
.. warning::

The default values for ``label`` and ``closed`` is '**left**' for all
frequency offsets except for 'ME', 'Y', 'Q', 'BM', 'BY', 'BQ', and 'W'
frequency offsets except for 'ME', 'Y', 'Q', 'BME', 'BY', 'BQ', and 'W'
which all have a default of 'right'.

This might unintendedly lead to looking ahead, where the value for a later
Expand Down
6 changes: 5 additions & 1 deletion doc/source/whatsnew/v2.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,11 @@ Other Deprecations
- Deprecated downcasting behavior in :meth:`Series.where`, :meth:`DataFrame.where`, :meth:`Series.mask`, :meth:`DataFrame.mask`, :meth:`Series.clip`, :meth:`DataFrame.clip`; in a future version these will not infer object-dtype columns to non-object dtype, or all-round floats to integer dtype. Call ``result.infer_objects(copy=False)`` on the result for object inference, or explicitly cast floats to ints. To opt in to the future version, use ``pd.set_option("future.no_silent_downcasting", True)`` (:issue:`53656`)
- Deprecated including the groups in computations when using :meth:`DataFrameGroupBy.apply` and :meth:`DataFrameGroupBy.resample`; pass ``include_groups=False`` to exclude the groups (:issue:`7155`)
- Deprecated not passing a tuple to :class:`DataFrameGroupBy.get_group` or :class:`SeriesGroupBy.get_group` when grouping by a length-1 list-like (:issue:`25971`)
- Deprecated string ``A`` denoting frequency in :class:`YearEnd` and strings ``A-DEC``, ``A-JAN``, etc. denoting annual frequencies with various fiscal year ends (:issue:`52536`)
- Deprecated string ``AS`` denoting frequency in :class:`YearBegin` and strings ``AS-DEC``, ``AS-JAN``, etc. denoting annual frequencies with various fiscal year starts (:issue:`54275`)
- Deprecated string ``A`` denoting frequency in :class:`YearEnd` and strings ``A-DEC``, ``A-JAN``, etc. denoting annual frequencies with various fiscal year ends (:issue:`54275`)
- Deprecated string ``BAS`` denoting frequency in :class:`BYearBegin` and strings ``BAS-DEC``, ``BAS-JAN``, etc. denoting annual frequencies with various fiscal year starts (:issue:`54275`)
- Deprecated string ``BA`` denoting frequency in :class:`BYearEnd` and strings ``BA-DEC``, ``BA-JAN``, etc. denoting annual frequencies with various fiscal year ends (:issue:`54275`)
- Deprecated strings ``BM``, and ``CBM`` denoting frequencies in :class:`BusinessMonthEnd`, :class:`CustomBusinessMonthEnd` (:issue:`52064`)
- Deprecated strings ``H``, ``BH``, and ``CBH`` denoting frequencies in :class:`Hour`, :class:`BusinessHour`, :class:`CustomBusinessHour` (:issue:`52536`)
- Deprecated strings ``H``, ``S``, ``U``, and ``N`` denoting units in :func:`to_timedelta` (:issue:`52536`)
- Deprecated strings ``H``, ``T``, ``S``, ``L``, ``U``, and ``N`` denoting units in :class:`Timedelta` (:issue:`52536`)
Expand Down
4 changes: 3 additions & 1 deletion pandas/_libs/tslibs/dtypes.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ cdef dict _abbrev_to_attrnames = {v: k for k, v in attrname_to_abbrevs.items()}
OFFSET_TO_PERIOD_FREQSTR: dict = {
"WEEKDAY": "D",
"EOM": "M",
"BM": "M",
"BME": "M",
"BQS": "Q",
"QS": "Q",
"BQ": "Q",
Expand Down Expand Up @@ -280,6 +280,8 @@ DEPR_ABBREVS: dict[str, str]= {
"BAS-SEP": "BYS-SEP",
"BAS-OCT": "BYS-OCT",
"BAS-NOV": "BYS-NOV",
"BM": "BME",
"CBM": "CBME",
"H": "h",
"BH": "bh",
"CBH": "cbh",
Expand Down
10 changes: 5 additions & 5 deletions pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2935,7 +2935,7 @@ cdef class BusinessMonthEnd(MonthOffset):
>>> pd.offsets.BMonthEnd().rollforward(ts)
Timestamp('2022-11-30 00:00:00')
"""
_prefix = "BM"
_prefix = "BME"
_day_opt = "business_end"


Expand Down Expand Up @@ -4465,10 +4465,10 @@ cdef class CustomBusinessMonthEnd(_CustomBusinessMonth):
>>> freq = pd.offsets.CustomBusinessMonthEnd(calendar=bdc)
>>> pd.date_range(dt.datetime(2022, 7, 10), dt.datetime(2022, 11, 10), freq=freq)
DatetimeIndex(['2022-07-29', '2022-08-31', '2022-09-29', '2022-10-28'],
dtype='datetime64[ns]', freq='CBM')
dtype='datetime64[ns]', freq='CBME')
"""

_prefix = "CBM"
_prefix = "CBME"


cdef class CustomBusinessMonthBegin(_CustomBusinessMonth):
Expand Down Expand Up @@ -4551,12 +4551,12 @@ prefix_mapping = {
BYearEnd, # 'BY'
BusinessDay, # 'B'
BusinessMonthBegin, # 'BMS'
BusinessMonthEnd, # 'BM'
BusinessMonthEnd, # 'BME'
BQuarterEnd, # 'BQ'
BQuarterBegin, # 'BQS'
BusinessHour, # 'bh'
CustomBusinessDay, # 'C'
CustomBusinessMonthEnd, # 'CBM'
CustomBusinessMonthEnd, # 'CBME'
CustomBusinessMonthBegin, # 'CBMS'
CustomBusinessHour, # 'cbh'
MonthEnd, # 'ME'
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -9187,11 +9187,11 @@ def resample(
Use frame.T.resample(...) instead.
closed : {{'right', 'left'}}, default None
Which side of bin interval is closed. The default is 'left'
for all frequency offsets except for 'ME', 'Y', 'Q', 'BM',
for all frequency offsets except for 'ME', 'Y', 'Q', 'BME',
'BA', 'BQ', and 'W' which all have a default of 'right'.
label : {{'right', 'left'}}, default None
Which bin edge label to label bucket with. The default is 'left'
for all frequency offsets except for 'ME', 'Y', 'Q', 'BM',
for all frequency offsets except for 'ME', 'Y', 'Q', 'BME',
'BA', 'BQ', and 'W' which all have a default of 'right'.
convention : {{'start', 'end', 's', 'e'}}, default 'start'
For `PeriodIndex` only, controls whether to use the start or
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -2101,7 +2101,7 @@ def __init__(
else:
freq = to_offset(freq)

end_types = {"ME", "Y", "Q", "BM", "BY", "BQ", "W"}
end_types = {"ME", "Y", "Q", "BME", "BY", "BQ", "W"}
rule = freq.rule_code
if rule in end_types or ("-" in rule and rule[: rule.find("-")] in end_types):
if closed is None:
Expand Down Expand Up @@ -2297,7 +2297,7 @@ def _adjust_bin_edges(
) -> tuple[DatetimeIndex, npt.NDArray[np.int64]]:
# Some hacks for > daily data, see #1471, #1458, #1483

if self.freq.name in ("BM", "ME", "W") or self.freq.name.split("-")[0] in (
if self.freq.name in ("BME", "ME", "W") or self.freq.name.split("-")[0] in (
"BQ",
"BY",
"Q",
Expand Down
10 changes: 5 additions & 5 deletions pandas/tests/frame/methods/test_asfreq.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ def test_asfreq2(self, frame_or_series):
datetime(2009, 11, 30),
datetime(2009, 12, 31),
],
freq="BM",
freq="BME",
),
)

daily_ts = ts.asfreq("B")
monthly_ts = daily_ts.asfreq("BM")
monthly_ts = daily_ts.asfreq("BME")
tm.assert_equal(monthly_ts, ts)

daily_ts = ts.asfreq("B", method="pad")
monthly_ts = daily_ts.asfreq("BM")
monthly_ts = daily_ts.asfreq("BME")
tm.assert_equal(monthly_ts, ts)

daily_ts = ts.asfreq(offsets.BDay())
Expand Down Expand Up @@ -140,12 +140,12 @@ def test_asfreq_resample_set_correct_freq(self, frame_or_series):
def test_asfreq_empty(self, datetime_frame):
# test does not blow up on length-0 DataFrame
zero_length = datetime_frame.reindex([])
result = zero_length.asfreq("BM")
result = zero_length.asfreq("BME")
assert result is not zero_length

def test_asfreq(self, datetime_frame):
offset_monthly = datetime_frame.asfreq(offsets.BMonthEnd())
rule_monthly = datetime_frame.asfreq("BM")
rule_monthly = datetime_frame.asfreq("BME")

tm.assert_frame_equal(offset_monthly, rule_monthly)

Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/indexes/datetimes/methods/test_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,6 @@ def test_shift_bmonth(self):

def test_shift_empty(self):
# GH#14811
dti = date_range(start="2016-10-21", end="2016-10-21", freq="BM")
dti = date_range(start="2016-10-21", end="2016-10-21", freq="BME")
result = dti.shift(1)
tm.assert_index_equal(result, dti)
2 changes: 1 addition & 1 deletion pandas/tests/indexes/datetimes/methods/test_to_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def test_to_period_annualish(self, off):
assert prng.freq == "Y-DEC"

def test_to_period_monthish(self):
offsets = ["MS", "BM"]
offsets = ["MS", "BME"]
for off in offsets:
rng = date_range("01-Jan-2012", periods=8, freq=off)
prng = rng.to_period()
Expand Down
14 changes: 14 additions & 0 deletions pandas/tests/indexes/datetimes/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,17 @@ def test_AS_BA_BAS_deprecated(self, freq_depr, expected_values, expected_freq):
)

tm.assert_index_equal(result, expected)

def test_BM_deprecated(self):
# GH#52064
msg = "'BM' is deprecated and will be removed in a future version."

with tm.assert_produces_warning(FutureWarning, match=msg):
expected = date_range(start="2016-02-21", end="2016-08-21", freq="2BM")
result = DatetimeIndex(
["2016-02-29", "2016-04-29", "2016-06-30"],
dtype="datetime64[ns]",
freq="2BME",
)

tm.assert_index_equal(result, expected)
2 changes: 1 addition & 1 deletion pandas/tests/indexes/datetimes/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def test_datetimeindex_accessors4(self):

def test_datetimeindex_accessors5(self):
freq_m = to_offset("ME")
bm = to_offset("BM")
bm = to_offset("BME")
qfeb = to_offset("Q-FEB")
qsfeb = to_offset("QS-FEB")
bq = to_offset("BQ")
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/indexes/multi/test_get_level_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def test_get_level_values_when_periods():

def test_values_loses_freq_of_underlying_index():
# GH#49054
idx = pd.DatetimeIndex(date_range("20200101", periods=3, freq="BM"))
idx = pd.DatetimeIndex(date_range("20200101", periods=3, freq="BME"))
expected = idx.copy(deep=True)
idx2 = Index([1, 2, 3])
midx = MultiIndex(levels=[idx, idx2], codes=[[0, 1, 2], [0, 1, 2]])
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/plotting/test_datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ def test_business_freq(self):
assert PeriodIndex(data=idx).freqstr == "B"

def test_business_freq_convert(self):
bts = tm.makeTimeSeries(300).asfreq("BM")
bts = tm.makeTimeSeries(300).asfreq("BME")
ts = bts.to_period("M")
_, ax = mpl.pyplot.subplots()
bts.plot(ax=ax)
Expand Down
15 changes: 13 additions & 2 deletions pandas/tests/resample/test_datetime_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,12 +498,12 @@ def test_resample_how_method(unit):

def test_resample_extra_index_point(unit):
# GH#9756
index = date_range(start="20150101", end="20150331", freq="BM").as_unit(unit)
index = date_range(start="20150101", end="20150331", freq="BME").as_unit(unit)
expected = DataFrame({"A": Series([21, 41, 63], index=index)})

index = date_range(start="20150101", end="20150331", freq="B").as_unit(unit)
df = DataFrame({"A": Series(range(len(index)), index=index)}, dtype="int64")
result = df.resample("BM").last()
result = df.resample("BME").last()
tm.assert_frame_equal(result, expected)


Expand Down Expand Up @@ -2020,6 +2020,17 @@ def test_resample_M_deprecated():
tm.assert_series_equal(result, expected)


def test_resample_BM_deprecated():
# GH#52064
depr_msg = "'BM' is deprecated and will be removed in a future version."

s = Series(range(10), index=date_range("20130101", freq="d", periods=10))
expected = s.resample("2BME").mean()
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
result = s.resample("2BM").mean()
tm.assert_series_equal(result, expected)


def test_resample_ms_closed_right():
# https://github.com/pandas-dev/pandas/issues/55271
dti = date_range(start="2020-01-31", freq="1min", periods=6000)
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/tseries/frequencies/test_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def base_delta_code_pair(request):
freqs = (
[f"Q-{month}" for month in MONTHS]
+ [f"{annual}-{month}" for annual in ["Y", "BY"] for month in MONTHS]
+ ["ME", "BM", "BMS"]
+ ["ME", "BME", "BMS"]
+ [f"WOM-{count}{day}" for count in range(1, 5) for day in DAYS]
+ [f"W-{day}" for day in DAYS]
)
Expand Down
8 changes: 4 additions & 4 deletions pandas/tests/tseries/offsets/test_offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ class TestOffsetNames:
def test_get_offset_name(self):
assert BDay().freqstr == "B"
assert BDay(2).freqstr == "2B"
assert BMonthEnd().freqstr == "BM"
assert BMonthEnd().freqstr == "BME"
assert Week(weekday=0).freqstr == "W-MON"
assert Week(weekday=1).freqstr == "W-TUE"
assert Week(weekday=2).freqstr == "W-WED"
Expand All @@ -776,8 +776,8 @@ def test_get_offset():
pairs = [
("B", BDay()),
("b", BDay()),
("bm", BMonthEnd()),
("Bm", BMonthEnd()),
("bme", BMonthEnd()),
("Bme", BMonthEnd()),
("W-MON", Week(weekday=0)),
("W-TUE", Week(weekday=1)),
("W-WED", Week(weekday=2)),
Expand Down Expand Up @@ -811,7 +811,7 @@ def test_alias_equality(self):
assert k == v.copy()

def test_rule_code(self):
lst = ["ME", "MS", "BM", "BMS", "D", "B", "h", "min", "s", "ms", "us"]
lst = ["ME", "MS", "BME", "BMS", "D", "B", "h", "min", "s", "ms", "us"]
for k in lst:
assert k == _get_offset(k).rule_code
# should be cached - this is kind of an internals test...
Expand Down
2 changes: 1 addition & 1 deletion pandas/tseries/frequencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def _get_monthly_rule(self) -> str | None:
if pos_check is None:
return None
else:
return {"cs": "MS", "bs": "BMS", "ce": "ME", "be": "BM"}.get(pos_check)
return {"cs": "MS", "bs": "BMS", "ce": "ME", "be": "BME"}.get(pos_check)

def _is_business_daily(self) -> bool:
# quick check: cannot be business daily
Expand Down

0 comments on commit 0021d24

Please sign in to comment.