diff --git a/doc/source/user_guide/timeseries.rst b/doc/source/user_guide/timeseries.rst index ca57e68d76015..105d11b67c9bf 100644 --- a/doc/source/user_guide/timeseries.rst +++ b/doc/source/user_guide/timeseries.rst @@ -891,7 +891,7 @@ into ``freq`` keyword arguments. The available date offsets and associated frequ :class:`~pandas.tseries.offsets.FY5253Quarter`, ``'REQ'``, "retail (aka 52-53 week) quarter" :class:`~pandas.tseries.offsets.YearEnd`, ``'YE'``, "calendar year end" :class:`~pandas.tseries.offsets.YearBegin`, ``'YS'`` or ``'BYS'``,"calendar year begin" - :class:`~pandas.tseries.offsets.BYearEnd`, ``'BY'``, "business year end" + :class:`~pandas.tseries.offsets.BYearEnd`, ``'BYE'``, "business year end" :class:`~pandas.tseries.offsets.BYearBegin`, ``'BYS'``, "business year begin" :class:`~pandas.tseries.offsets.FY5253`, ``'RE'``, "retail (aka 52-53 week) year" :class:`~pandas.tseries.offsets.Easter`, None, "Easter holiday" @@ -1253,7 +1253,7 @@ frequencies. We will refer to these aliases as *offset aliases*. "QS", "quarter start frequency" "BQS", "business quarter start frequency" "YE", "year end frequency" - "BY", "business year end frequency" + "BYE", "business year end frequency" "YS", "year start frequency" "BYS", "business year start frequency" "h", "hourly frequency" @@ -1686,7 +1686,7 @@ the end of the interval. .. warning:: The default values for ``label`` and ``closed`` is '**left**' for all - frequency offsets except for 'ME', 'YE', 'QE', 'BME', 'BY', 'BQE', and 'W' + frequency offsets except for 'ME', 'YE', 'QE', 'BME', 'BYE', 'BQE', and 'W' which all have a default of 'right'. This might unintendedly lead to looking ahead, where the value for a later diff --git a/doc/source/whatsnew/v2.2.0.rst b/doc/source/whatsnew/v2.2.0.rst index 749e3bf9dc373..43771e6536046 100644 --- a/doc/source/whatsnew/v2.2.0.rst +++ b/doc/source/whatsnew/v2.2.0.rst @@ -233,8 +233,8 @@ Other API changes Deprecations ~~~~~~~~~~~~ -Deprecate aliases ``M``, ``SM``, ``BM``, ``CBM``, ``Q``, ``BQ``, and ``Y`` in favour of ``ME``, ``SME``, ``BME``, ``CBME``, ``QE``, ``BQE``, and ``YE`` for offsets -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Deprecate aliases ``M``, ``SM``, ``BM``, ``CBM``, ``Q``, ``BQ``, ``Y``, and ``BY`` in favour of ``ME``, ``SME``, ``BME``, ``CBME``, ``QE``, ``BQE``, ``YE``, and ``BYE`` for offsets +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Deprecated the following frequency aliases (:issue:`9586`): @@ -245,6 +245,7 @@ Deprecated the following frequency aliases (:issue:`9586`): - ``Q`` (quarter end) has been renamed ``QE`` for offsets - ``BQ`` (business quarter end) has been renamed ``BQE`` for offsets - ``Y`` (year end) has been renamed ``YE`` for offsets +- ``BY`` (business year end) has been renamed ``BYE`` for offsets For example: @@ -295,8 +296,6 @@ Other Deprecations - 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 string ``BQ`` denoting frequency in :class:`BQuarterEnd` (:issue:`52064`) -- Deprecated strings ``BM``, ``CBM``, and ``SM`` denoting frequencies in :class:`BusinessMonthEnd`, :class:`CustomBusinessMonthEnd, :class:`SemiMonthEnd` (: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`) diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index ffb1afe8720e1..17f517e5e7264 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -229,7 +229,19 @@ OFFSET_TO_PERIOD_FREQSTR: dict = { "W": "W", "ME": "M", "Y": "Y", - "BY": "Y", + "BYE": "Y", + "BYE-DEC": "Y-DEC", + "BYE-JAN": "Y-JAN", + "BYE-FEB": "Y-FEB", + "BYE-MAR": "Y-MAR", + "BYE-APR": "Y-APR", + "BYE-MAY": "Y-MAY", + "BYE-JUN": "Y-JUN", + "BYE-JUL": "Y-JUL", + "BYE-AUG": "Y-AUG", + "BYE-SEP": "Y-SEP", + "BYE-OCT": "Y-OCT", + "BYE-NOV": "Y-NOV", "YS": "Y", "BYS": "Y", } @@ -274,6 +286,32 @@ cdef dict c_OFFSET_DEPR_FREQSTR = { "A-SEP": "YE-SEP", "A-OCT": "YE-OCT", "A-NOV": "YE-NOV", + "BY": "BYE", + "BY-DEC": "BYE-DEC", + "BY-JAN": "BYE-JAN", + "BY-FEB": "BYE-FEB", + "BY-MAR": "BYE-MAR", + "BY-APR": "BYE-APR", + "BY-MAY": "BYE-MAY", + "BY-JUN": "BYE-JUN", + "BY-JUL": "BYE-JUL", + "BY-AUG": "BYE-AUG", + "BY-SEP": "BYE-SEP", + "BY-OCT": "BYE-OCT", + "BY-NOV": "BYE-NOV", + "BA": "BYE", + "BA-DEC": "BYE-DEC", + "BA-JAN": "BYE-JAN", + "BA-FEB": "BYE-FEB", + "BA-MAR": "BYE-MAR", + "BA-APR": "BYE-APR", + "BA-MAY": "BYE-MAY", + "BA-JUN": "BYE-JUN", + "BA-JUL": "BYE-JUL", + "BA-AUG": "BYE-AUG", + "BA-SEP": "BYE-SEP", + "BA-OCT": "BYE-OCT", + "BA-NOV": "BYE-NOV", "BM": "BME", "CBM": "CBME", "SM": "SME", diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index b79b8b23118a6..01962c47ff31c 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -2405,7 +2405,7 @@ cdef class BYearEnd(YearOffset): _outputName = "BusinessYearEnd" _default_month = 12 - _prefix = "BY" + _prefix = "BYE" _day_opt = "business_end" @@ -4535,7 +4535,7 @@ prefix_mapping = { YearBegin, # 'YS' YearEnd, # 'YE' BYearBegin, # 'BYS' - BYearEnd, # 'BY' + BYearEnd, # 'BYE' BusinessDay, # 'B' BusinessMonthBegin, # 'BMS' BusinessMonthEnd, # 'BME' @@ -4577,7 +4577,7 @@ _lite_rule_alias = { "YE": "YE-DEC", # YearEnd(month=12), "YS": "YS-JAN", # YearBegin(month=1), - "BY": "BY-DEC", # BYearEnd(month=12), + "BYE": "BYE-DEC", # BYearEnd(month=12), "BYS": "BYS-JAN", # BYearBegin(month=1), "Min": "min", diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 6a95457526b5a..8af81cd43d62e 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -2130,7 +2130,7 @@ def __init__( else: freq = to_offset(freq) - end_types = {"ME", "YE", "QE", "BME", "BY", "BQE", "W"} + end_types = {"ME", "YE", "QE", "BME", "BYE", "BQE", "W"} rule = freq.rule_code if rule in end_types or ("-" in rule and rule[: rule.find("-")] in end_types): if closed is None: @@ -2328,7 +2328,7 @@ def _adjust_bin_edges( if self.freq.name in ("BME", "ME", "W") or self.freq.name.split("-")[0] in ( "BQE", - "BY", + "BYE", "QE", "YE", "W", diff --git a/pandas/tests/frame/methods/test_asfreq.py b/pandas/tests/frame/methods/test_asfreq.py index a004fb9d92ffc..6926917a09602 100644 --- a/pandas/tests/frame/methods/test_asfreq.py +++ b/pandas/tests/frame/methods/test_asfreq.py @@ -247,6 +247,7 @@ def test_asfreq_2ME(self, freq, freq_half): ("2YE-MAR", "2Y-MAR"), ("1YE", "1A"), ("2YE-MAR", "2A-MAR"), + ("2BYE-MAR", "2BA-MAR"), ], ) def test_asfreq_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr): diff --git a/pandas/tests/indexes/datetimes/methods/test_to_period.py b/pandas/tests/indexes/datetimes/methods/test_to_period.py index 206524e77c2a5..aa217e895c30a 100644 --- a/pandas/tests/indexes/datetimes/methods/test_to_period.py +++ b/pandas/tests/indexes/datetimes/methods/test_to_period.py @@ -56,7 +56,7 @@ def test_to_period_quarterlyish(self, off): prng = rng.to_period() assert prng.freq == "QE-DEC" - @pytest.mark.parametrize("off", ["BY", "YS", "BYS"]) + @pytest.mark.parametrize("off", ["BYE", "YS", "BYS"]) def test_to_period_annualish(self, off): rng = date_range("01-Jan-2012", periods=8, freq=off) prng = rng.to_period() @@ -110,6 +110,22 @@ def test_to_period_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr): with tm.assert_produces_warning(FutureWarning, match=msg): assert prng.freq == freq_depr + @pytest.mark.parametrize( + "freq, freq_depr", + [ + ("2BQE-SEP", "2BQ-SEP"), + ("2BYE-MAR", "2BY-MAR"), + ], + ) + def test_to_period_frequency_BQ_BY_deprecated(self, freq, freq_depr): + # GH#9586 + msg = f"'{freq_depr[1:]}' is deprecated, please use '{freq[1:]}' instead." + + rng = date_range("01-Jan-2012", periods=8, freq=freq) + prng = rng.to_period() + with tm.assert_produces_warning(FutureWarning, match=msg): + prng.freq == freq_depr + def test_to_period_infer(self): # https://github.com/pandas-dev/pandas/issues/33358 rng = date_range( diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index 80b5880257483..bb46d6d2b67f7 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -150,9 +150,11 @@ def test_date_range_float_periods(self): [ ("2ME", "2M"), ("2SME", "2SM"), + ("2BQE", "2BQ"), + ("2BYE", "2BY"), ], ) - def test_date_range_frequency_M_SM_deprecated(self, freq, freq_depr): + def test_date_range_frequency_M_SM_BQ_BY_deprecated(self, freq, freq_depr): # GH#52064 depr_msg = f"'{freq_depr[1:]}' is deprecated, please use '{freq[1:]}' instead." @@ -1540,11 +1542,11 @@ def test_date_range_negative_freq_year_end(self, unit): def test_date_range_business_year_end_year(self, unit): # see GH#9313 - rng = date_range("1/1/2013", "7/1/2017", freq="BY", unit=unit) + rng = date_range("1/1/2013", "7/1/2017", freq="BYE", unit=unit) exp = DatetimeIndex( ["2013-12-31", "2014-12-31", "2015-12-31", "2016-12-30"], dtype=f"M8[{unit}]", - freq="BY", + freq="BYE", ) tm.assert_index_equal(rng, exp) diff --git a/pandas/tests/indexes/datetimes/test_datetime.py b/pandas/tests/indexes/datetimes/test_datetime.py index 495af8dfc9eb8..d2d627c27312a 100644 --- a/pandas/tests/indexes/datetimes/test_datetime.py +++ b/pandas/tests/indexes/datetimes/test_datetime.py @@ -161,11 +161,6 @@ def test_CBH_deprecated(self): @pytest.mark.parametrize( "freq_depr, expected_values, expected_freq", [ - ( - "2BA", - ["2020-12-31", "2022-12-30"], - "2BY-DEC", - ), ( "AS-AUG", ["2021-08-01", "2022-08-01", "2023-08-01"], @@ -178,7 +173,7 @@ def test_CBH_deprecated(self): ), ], ) - def test_AS_BA_BAS_deprecated(self, freq_depr, expected_values, expected_freq): + def test_AS_BAS_deprecated(self, freq_depr, expected_values, expected_freq): # GH#55479 freq_msg = re.split("[0-9]*", freq_depr, maxsplit=1)[1] msg = f"'{freq_msg}' is deprecated and will be removed in a future version." @@ -198,12 +193,14 @@ def test_AS_BA_BAS_deprecated(self, freq_depr, expected_values, expected_freq): @pytest.mark.parametrize( "freq, expected_values, freq_depr", [ + ("2BYE-MAR", ["2016-03-31"], "2BA-MAR"), + ("2BYE-JUN", ["2016-06-30"], "2BY-JUN"), ("2BME", ["2016-02-29", "2016-04-29", "2016-06-30"], "2BM"), ("2BQE", ["2016-03-31"], "2BQ"), ("1BQE-MAR", ["2016-03-31", "2016-06-30"], "1BQ-MAR"), ], ) - def test_BM_BQ_deprecated(self, freq, expected_values, freq_depr): + def test_BM_BQ_BY_deprecated(self, freq, expected_values, freq_depr): # GH#52064 msg = f"'{freq_depr[1:]}' is deprecated, please use '{freq[1:]}' instead." diff --git a/pandas/tests/resample/test_period_index.py b/pandas/tests/resample/test_period_index.py index f3d095bf4b5ed..4d9c47c29dd31 100644 --- a/pandas/tests/resample/test_period_index.py +++ b/pandas/tests/resample/test_period_index.py @@ -931,13 +931,21 @@ def test_resample_t_l_deprecated(self): @pytest.mark.parametrize( - "freq_depr", ["2ME", "2QE", "2QE-FEB", "2BQE", "2BQE-FEB", "2YE", "2YE-MAR"] + "freq,freq_depr", + [ + ("2M", "2ME"), + ("2Q", "2QE"), + ("2Q-FEB", "2QE-FEB"), + ("2BQ", "2BQE"), + ("2BQ-FEB", "2BQE-FEB"), + ("2Y", "2YE"), + ("2Y-MAR", "2YE-MAR"), + ("2BA-MAR", "2BYE-MAR"), + ], ) -def test_resample_frequency_ME_QE_error_message(series_and_frame, freq_depr): +def test_resample_frequency_ME_QE_YE_error_message(series_and_frame, freq, freq_depr): # GH#9586 - pos_e = freq_depr.index("E") - msg = f"for Period, please use '{freq_depr[1:pos_e]}{freq_depr[pos_e+1:]}' " - f"instead of '{freq_depr[1:]}'" + msg = f"for Period, please use '{freq[1:]}' instead of '{freq_depr[1:]}'" obj = series_and_frame with pytest.raises(ValueError, match=msg): diff --git a/pandas/tests/tseries/frequencies/test_inference.py b/pandas/tests/tseries/frequencies/test_inference.py index 19c6425c12ce1..ee8f161858b2b 100644 --- a/pandas/tests/tseries/frequencies/test_inference.py +++ b/pandas/tests/tseries/frequencies/test_inference.py @@ -52,7 +52,7 @@ def base_delta_code_pair(request): freqs = ( [f"QE-{month}" for month in MONTHS] - + [f"{annual}-{month}" for annual in ["YE", "BY"] for month in MONTHS] + + [f"{annual}-{month}" for annual in ["YE", "BYE"] for month in MONTHS] + ["ME", "BME", "BMS"] + [f"WOM-{count}{day}" for count in range(1, 5) for day in DAYS] + [f"W-{day}" for day in DAYS] diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index bc20e840b7c61..21232caa398fa 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -820,7 +820,7 @@ def test_rule_code(self): "NOV", "DEC", ] - base_lst = ["YE", "YS", "BY", "BYS", "QE", "QS", "BQE", "BQS"] + base_lst = ["YE", "YS", "BYE", "BYS", "QE", "QS", "BQE", "BQS"] for base in base_lst: for v in suffix_lst: alias = "-".join([base, v]) @@ -839,7 +839,7 @@ def test_freq_offsets(): class TestReprNames: def test_str_for_named_is_name(self): # look at all the amazing combinations! - month_prefixes = ["YE", "YS", "BY", "BYS", "QE", "BQE", "BQS", "QS"] + month_prefixes = ["YE", "YS", "BYE", "BYS", "QE", "BQE", "BQS", "QS"] names = [ prefix + "-" + month for prefix in month_prefixes diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index d6ed3b1e90642..4a1a668426b36 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -59,7 +59,7 @@ # -------------------------------------------------------------------- # Offset related functions -_need_suffix = ["QS", "BQE", "BQS", "YS", "BY", "BYS"] +_need_suffix = ["QS", "BQE", "BQS", "YS", "BYE", "BYS"] for _prefix in _need_suffix: for _m in MONTHS: @@ -345,7 +345,7 @@ def _get_annual_rule(self) -> str | None: if pos_check is None: return None else: - return {"cs": "YS", "bs": "BYS", "ce": "YE", "be": "BY"}.get(pos_check) + return {"cs": "YS", "bs": "BYS", "ce": "YE", "be": "BYE"}.get(pos_check) def _get_quarterly_rule(self) -> str | None: if len(self.mdiffs) > 1: