From 9b12f052c54e6076e43ad7b4fe3c3b4e3a37306d Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Fri, 1 Mar 2024 23:22:36 +0100 Subject: [PATCH 1/4] correct def to_offset, get_reso_from_freqstr, move freq A to c_REMOVED_ABBREVS --- pandas/_libs/tslibs/dtypes.pxd | 1 + pandas/_libs/tslibs/dtypes.pyx | 35 ++++++++++++++++++++------------- pandas/_libs/tslibs/offsets.pyx | 13 ++++++------ 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/pandas/_libs/tslibs/dtypes.pxd b/pandas/_libs/tslibs/dtypes.pxd index 33f6789f3b402..3883a18d670f0 100644 --- a/pandas/_libs/tslibs/dtypes.pxd +++ b/pandas/_libs/tslibs/dtypes.pxd @@ -18,6 +18,7 @@ cdef dict c_DEPR_ABBREVS cdef dict attrname_to_abbrevs cdef dict npy_unit_to_attrname cdef dict attrname_to_npy_unit +cdef set c_REMOVED_ABBREVS cdef enum c_FreqGroup: # Mirrors FreqGroup in the .pyx file diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index c0d1b2e79f587..be56b38f23094 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -336,20 +336,6 @@ cdef dict c_REVERSE_OFFSET_DEPR_FREQSTR = { # Map deprecated resolution abbreviations to correct resolution abbreviations cdef dict c_DEPR_ABBREVS = { - "A": "Y", - "a": "Y", - "A-DEC": "Y-DEC", - "A-JAN": "Y-JAN", - "A-FEB": "Y-FEB", - "A-MAR": "Y-MAR", - "A-APR": "Y-APR", - "A-MAY": "Y-MAY", - "A-JUN": "Y-JUN", - "A-JUL": "Y-JUL", - "A-AUG": "Y-AUG", - "A-SEP": "Y-SEP", - "A-OCT": "Y-OCT", - "A-NOV": "Y-NOV", "BA": "BY", "BA-DEC": "BY-DEC", "BA-JAN": "BY-JAN", @@ -403,6 +389,23 @@ cdef dict c_DEPR_ABBREVS = { "n": "ns", } +cdef set c_REMOVED_ABBREVS = { + "A", + "a", + "A-DEC", + "A-JAN", + "A-FEB", + "A-MAR", + "A-APR", + "A-MAY", + "A-JUN", + "A-JUL", + "A-AUG", + "A-SEP", + "A-OCT", + "A-NOV", +} + class FreqGroup(Enum): # Mirrors c_FreqGroup in the .pxd file @@ -494,6 +497,10 @@ class Resolution(Enum): """ cdef: str abbrev + if freq == "A": + raise ValueError( + "Frequency \'A\' is no longer supported." + ) try: if freq in c_DEPR_ABBREVS: abbrev = c_DEPR_ABBREVS[freq] diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 5971927a4dad8..b09d7e5567699 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -58,6 +58,7 @@ from pandas._libs.tslibs.conversion cimport localize_pydatetime from pandas._libs.tslibs.dtypes cimport ( c_DEPR_ABBREVS, c_OFFSET_DEPR_FREQSTR, + c_REMOVED_ABBREVS, c_REVERSE_OFFSET_DEPR_FREQSTR, periods_per_day, ) @@ -4886,12 +4887,8 @@ cpdef to_offset(freq, bint is_period=False): ) elif is_period and name.upper() in c_OFFSET_DEPR_FREQSTR: if name.upper().startswith("A"): - warnings.warn( - f"\'{name}\' is deprecated and will be removed in a future " - f"version, please use " - f"\'{c_DEPR_ABBREVS.get(name.upper())}\' instead.", - FutureWarning, - stacklevel=find_stack_level(), + raise ValueError( + f"Frequency \'{name}\' is no longer supported." ) if name.upper() != name: warnings.warn( @@ -4911,6 +4908,10 @@ cpdef to_offset(freq, bint is_period=False): if not stride: stride = 1 + if prefix in c_REMOVED_ABBREVS: + raise ValueError( + f"Frequency \'{prefix}\' is no longer supported." + ) if prefix in c_DEPR_ABBREVS: warnings.warn( f"\'{prefix}\' is deprecated and will be removed " From aa7fa2b28426df82c5f1bf39520caa6bcfc711a1 Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Sun, 3 Mar 2024 10:41:38 +0100 Subject: [PATCH 2/4] remove A, A-DEC, etc. from c_OFFSET_DEPR_FREQSTR and c_REMOVED_ABBREVS, fix resolution test --- pandas/_libs/tslibs/dtypes.pxd | 1 - pandas/_libs/tslibs/dtypes.pyx | 34 -------------------------- pandas/_libs/tslibs/offsets.pyx | 9 ------- pandas/tests/tslibs/test_resolution.py | 4 +-- 4 files changed, 2 insertions(+), 46 deletions(-) diff --git a/pandas/_libs/tslibs/dtypes.pxd b/pandas/_libs/tslibs/dtypes.pxd index 3883a18d670f0..33f6789f3b402 100644 --- a/pandas/_libs/tslibs/dtypes.pxd +++ b/pandas/_libs/tslibs/dtypes.pxd @@ -18,7 +18,6 @@ cdef dict c_DEPR_ABBREVS cdef dict attrname_to_abbrevs cdef dict npy_unit_to_attrname cdef dict attrname_to_npy_unit -cdef set c_REMOVED_ABBREVS cdef enum c_FreqGroup: # Mirrors FreqGroup in the .pyx file diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index be56b38f23094..6a81681369fb7 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -273,19 +273,6 @@ cdef dict c_OFFSET_DEPR_FREQSTR = { "Y-SEP": "YE-SEP", "Y-OCT": "YE-OCT", "Y-NOV": "YE-NOV", - "A": "YE", - "A-DEC": "YE-DEC", - "A-JAN": "YE-JAN", - "A-FEB": "YE-FEB", - "A-MAR": "YE-MAR", - "A-APR": "YE-APR", - "A-MAY": "YE-MAY", - "A-JUN": "YE-JUN", - "A-JUL": "YE-JUL", - "A-AUG": "YE-AUG", - "A-SEP": "YE-SEP", - "A-OCT": "YE-OCT", - "A-NOV": "YE-NOV", "BY": "BYE", "BY-DEC": "BYE-DEC", "BY-JAN": "BYE-JAN", @@ -389,23 +376,6 @@ cdef dict c_DEPR_ABBREVS = { "n": "ns", } -cdef set c_REMOVED_ABBREVS = { - "A", - "a", - "A-DEC", - "A-JAN", - "A-FEB", - "A-MAR", - "A-APR", - "A-MAY", - "A-JUN", - "A-JUL", - "A-AUG", - "A-SEP", - "A-OCT", - "A-NOV", -} - class FreqGroup(Enum): # Mirrors c_FreqGroup in the .pxd file @@ -497,10 +467,6 @@ class Resolution(Enum): """ cdef: str abbrev - if freq == "A": - raise ValueError( - "Frequency \'A\' is no longer supported." - ) try: if freq in c_DEPR_ABBREVS: abbrev = c_DEPR_ABBREVS[freq] diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index b09d7e5567699..fd18ae5908f10 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -58,7 +58,6 @@ from pandas._libs.tslibs.conversion cimport localize_pydatetime from pandas._libs.tslibs.dtypes cimport ( c_DEPR_ABBREVS, c_OFFSET_DEPR_FREQSTR, - c_REMOVED_ABBREVS, c_REVERSE_OFFSET_DEPR_FREQSTR, periods_per_day, ) @@ -4886,10 +4885,6 @@ cpdef to_offset(freq, bint is_period=False): f"instead of \'{name}\'" ) elif is_period and name.upper() in c_OFFSET_DEPR_FREQSTR: - if name.upper().startswith("A"): - raise ValueError( - f"Frequency \'{name}\' is no longer supported." - ) if name.upper() != name: warnings.warn( f"\'{name}\' is deprecated and will be removed in " @@ -4908,10 +4903,6 @@ cpdef to_offset(freq, bint is_period=False): if not stride: stride = 1 - if prefix in c_REMOVED_ABBREVS: - raise ValueError( - f"Frequency \'{prefix}\' is no longer supported." - ) if prefix in c_DEPR_ABBREVS: warnings.warn( f"\'{prefix}\' is deprecated and will be removed " diff --git a/pandas/tests/tslibs/test_resolution.py b/pandas/tests/tslibs/test_resolution.py index 690962f1daa5e..c91e7bd6574ff 100644 --- a/pandas/tests/tslibs/test_resolution.py +++ b/pandas/tests/tslibs/test_resolution.py @@ -48,8 +48,8 @@ def test_get_attrname_from_abbrev(freqstr, expected): assert reso.attrname == expected -@pytest.mark.parametrize("freq", ["A", "H", "T", "S", "L", "U", "N"]) -def test_units_A_H_T_S_L_U_N_deprecated_from_attrname_to_abbrevs(freq): +@pytest.mark.parametrize("freq", ["H", "T", "S", "L", "U", "N"]) +def test_units_H_T_S_L_U_N_deprecated_from_attrname_to_abbrevs(freq): # GH#52536 msg = f"'{freq}' is deprecated and will be removed in a future version." From e16a045b068b81cc1b03c341a476234da6c8d78c Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Wed, 6 Mar 2024 21:22:14 +0100 Subject: [PATCH 3/4] fix tests --- pandas/tests/arrays/test_datetimes.py | 10 +++++--- pandas/tests/frame/methods/test_asfreq.py | 18 ++++++++++++-- .../datetimes/methods/test_to_period.py | 4 +--- .../indexes/datetimes/test_date_range.py | 12 +++++++--- .../tests/indexes/period/test_period_range.py | 24 ++++++------------- pandas/tests/resample/test_datetime_index.py | 13 +++++++--- 6 files changed, 50 insertions(+), 31 deletions(-) diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 8f0576cc65a27..51dc032b072fe 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -772,11 +772,8 @@ def test_iter_zoneinfo_fold(self, tz): ("2QE-SEP", "2Q-SEP"), ("1YE", "1Y"), ("2YE-MAR", "2Y-MAR"), - ("1YE", "1A"), - ("2YE-MAR", "2A-MAR"), ("2ME", "2m"), ("2QE-SEP", "2q-sep"), - ("2YE-MAR", "2a-mar"), ("2YE", "2y"), ], ) @@ -826,6 +823,13 @@ def test_date_range_lowercase_frequency_deprecated(self, freq_depr): result = pd.date_range("1/1/2000", periods=4, freq=freq_depr) tm.assert_index_equal(result, expected) + @pytest.mark.parametrize("freq", ["1A", "2A-MAR", "2a-mar"]) + def test_date_range_frequency_A_raises(self, freq): + msg = f"Invalid frequency: {freq}" + + with pytest.raises(ValueError, match=msg): + pd.date_range("1/1/2000", periods=4, freq=freq) + def test_factorize_sort_without_freq(): dta = DatetimeArray._from_sequence([0, 2, 1], dtype="M8[ns]") diff --git a/pandas/tests/frame/methods/test_asfreq.py b/pandas/tests/frame/methods/test_asfreq.py index f6b71626b6fee..dd029689e83d3 100644 --- a/pandas/tests/frame/methods/test_asfreq.py +++ b/pandas/tests/frame/methods/test_asfreq.py @@ -242,8 +242,6 @@ def test_asfreq_2ME(self, freq, freq_half): ("2BQE-SEP", "2BQ-SEP"), ("1YE", "1Y"), ("2YE-MAR", "2Y-MAR"), - ("1YE", "1A"), - ("2YE-MAR", "2A-MAR"), ("2BYE-MAR", "2BA-MAR"), ], ) @@ -283,3 +281,19 @@ def test_asfreq_unsupported_freq(self, freq, error_msg): with pytest.raises(ValueError, match=error_msg): df.asfreq(freq=freq) + + @pytest.mark.parametrize( + "freq, freq_depr", + [ + ("1YE", "1A"), + ("2YE-MAR", "2A-MAR"), + ], + ) + def test_asfreq_frequency_A_raisees(self, freq, freq_depr): + msg = f"Invalid frequency: {freq_depr[1:]}" + + index = date_range("1/1/2000", periods=4, freq=f"{freq[1:]}") + df = DataFrame({"s": Series([0.0, 1.0, 2.0, 3.0], index=index)}) + + with pytest.raises(ValueError, match=msg): + df.asfreq(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 de8d32f64cde2..05e9a294d74a6 100644 --- a/pandas/tests/indexes/datetimes/methods/test_to_period.py +++ b/pandas/tests/indexes/datetimes/methods/test_to_period.py @@ -97,11 +97,9 @@ def test_dti_to_period_2monthish(self, freq_offset, freq_period): ("2QE-SEP", "2Q-SEP"), ("1YE", "1Y"), ("2YE-MAR", "2Y-MAR"), - ("1YE", "1A"), - ("2YE-MAR", "2A-MAR"), ], ) - def test_to_period_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr): + def test_to_period_frequency_M_Q_Y_deprecated(self, freq, freq_depr): # GH#9586 msg = f"'{freq_depr[1:]}' is deprecated and will be removed " f"in a future version, please use '{freq[1:]}' instead." diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index e26f35f4e8258..fecd7f4e7f2b0 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -800,13 +800,11 @@ def test_frequencies_H_T_S_L_U_N_deprecated(self, freq, freq_depr): @pytest.mark.parametrize( "freq,freq_depr", [ - ("200YE", "200A"), ("YE", "Y"), - ("2YE-MAY", "2A-MAY"), ("YE-MAY", "Y-MAY"), ], ) - def test_frequencies_A_deprecated_Y_renamed(self, freq, freq_depr): + def test_frequencies_Y_renamed(self, freq, freq_depr): # GH#9586, GH#54275 freq_msg = re.split("[0-9]*", freq, maxsplit=1)[1] freq_depr_msg = re.split("[0-9]*", freq_depr, maxsplit=1)[1] @@ -836,6 +834,14 @@ def test_date_range_bday(self): assert idx[0] == sdate + 0 * offsets.BDay() assert idx.freq == "B" + @pytest.mark.parametrize("freq", ["200A", "2A-MAY"]) + def test_frequency_A_raises(self, freq): + freq_msg = re.split("[0-9]*", freq, maxsplit=1)[1] + msg = f"Invalid frequency: {freq_msg}" + + with pytest.raises(ValueError, match=msg): + date_range("1/1/2000", periods=2, freq=freq) + class TestDateRangeTZ: """Tests for date_range with timezones""" diff --git a/pandas/tests/indexes/period/test_period_range.py b/pandas/tests/indexes/period/test_period_range.py index 6f8e6d07da8bf..fb200d071951e 100644 --- a/pandas/tests/indexes/period/test_period_range.py +++ b/pandas/tests/indexes/period/test_period_range.py @@ -205,23 +205,6 @@ def test_constructor_U(self): with pytest.raises(ValueError, match="Invalid frequency: X"): period_range("2007-1-1", periods=500, freq="X") - @pytest.mark.parametrize( - "freq,freq_depr", - [ - ("2Y", "2A"), - ("2Y", "2a"), - ("2Y-AUG", "2A-AUG"), - ("2Y-AUG", "2A-aug"), - ], - ) - def test_a_deprecated_from_time_series(self, freq, freq_depr): - # GH#52536 - msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a " - f"future version. Please use '{freq[1:]}' instead." - - with tm.assert_produces_warning(FutureWarning, match=msg): - period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009") - @pytest.mark.parametrize("freq_depr", ["2H", "2MIN", "2S", "2US", "2NS"]) def test_uppercase_freq_deprecated_from_time_series(self, freq_depr): # GH#52536, GH#54939 @@ -239,3 +222,10 @@ def test_lowercase_freq_deprecated_from_time_series(self, freq_depr): with tm.assert_produces_warning(FutureWarning, match=msg): period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009") + + @pytest.mark.parametrize("freq", ["2A", "2a", "2A-AUG", "2A-aug"]) + def test_A_raises_from_time_series(self, freq): + msg = f"Invalid frequency: {freq}" + + with pytest.raises(ValueError, match=msg): + period_range(freq=freq, start="1/1/2001", end="12/1/2009") diff --git a/pandas/tests/resample/test_datetime_index.py b/pandas/tests/resample/test_datetime_index.py index 461b6bfc3b420..0ee5ee4ec137d 100644 --- a/pandas/tests/resample/test_datetime_index.py +++ b/pandas/tests/resample/test_datetime_index.py @@ -2047,11 +2047,9 @@ def test_resample_empty_series_with_tz(): ("2QE-SEP", "2Q-SEP"), ("1YE", "1Y"), ("2YE-MAR", "2Y-MAR"), - ("1YE", "1A"), - ("2YE-MAR", "2A-MAR"), ], ) -def test_resample_M_Q_Y_A_deprecated(freq, freq_depr): +def test_resample_M_Q_Y_deprecated(freq, freq_depr): # GH#9586 depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed " f"in a future version, please use '{freq[1:]}' instead." @@ -2174,3 +2172,12 @@ def test_arrow_timestamp_resample(tz): expected = Series(np.arange(5, dtype=np.float64), index=idx) result = expected.resample("1D").mean() tm.assert_series_equal(result, expected) + + +@pytest.mark.parametrize("freq", ["1A", "2A-MAR"]) +def test_resample_A_raises(freq): + msg = f"Invalid frequency: {freq[1:]}" + + s = Series(range(10), index=date_range("20130101", freq="d", periods=10)) + with pytest.raises(ValueError, match=msg): + s.resample(freq).mean() From ae1928b0aac0ebcefe2158891c8a837bab242b9b Mon Sep 17 00:00:00 2001 From: Natalia Mokeeva Date: Thu, 7 Mar 2024 13:47:05 +0100 Subject: [PATCH 4/4] simplify test_asfreq_frequency_A_raises, add a note to 3.0.0, correct timeseries.rst --- doc/source/user_guide/timeseries.rst | 4 ++-- doc/source/whatsnew/v3.0.0.rst | 1 + pandas/tests/frame/methods/test_asfreq.py | 15 ++++----------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/doc/source/user_guide/timeseries.rst b/doc/source/user_guide/timeseries.rst index 0f0e6271d8329..0f38d90e18616 100644 --- a/doc/source/user_guide/timeseries.rst +++ b/doc/source/user_guide/timeseries.rst @@ -1327,8 +1327,8 @@ frequencies. We will refer to these aliases as *period aliases*. .. deprecated:: 2.2.0 - Aliases ``A``, ``H``, ``T``, ``S``, ``L``, ``U``, and ``N`` are deprecated in favour of the aliases - ``Y``, ``h``, ``min``, ``s``, ``ms``, ``us``, and ``ns``. + Aliases ``H``, ``T``, ``S``, ``L``, ``U``, and ``N`` are deprecated in favour of the aliases + ``h``, ``min``, ``s``, ``ms``, ``us``, and ``ns``. Combining aliases diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 7802ef4798659..f2644a2435ba3 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -199,6 +199,7 @@ Removal of prior version deprecations/changes - Changed the default value of ``observed`` in :meth:`DataFrame.groupby` and :meth:`Series.groupby` to ``True`` (:issue:`51811`) - Enforced deprecation disallowing parsing datetimes with mixed time zones unless user passes ``utc=True`` to :func:`to_datetime` (:issue:`57275`) - Enforced deprecation of ``axis=None`` acting the same as ``axis=0`` in the DataFrame reductions ``sum``, ``prod``, ``std``, ``var``, and ``sem``, passing ``axis=None`` will now reduce over both axes; this is particularly the case when doing e.g. ``numpy.sum(df)`` (:issue:`21597`) +- Enforced deprecation of string ``A`` denoting frequency in :class:`YearEnd` and strings ``A-DEC``, ``A-JAN``, etc. denoting annual frequencies with various fiscal year ends (:issue:`57699`) - Enforced silent-downcasting deprecation for :ref:`all relevant methods ` (:issue:`54710`) - In :meth:`DataFrame.stack`, the default value of ``future_stack`` is now ``True``; specifying ``False`` will raise a ``FutureWarning`` (:issue:`55448`) - Methods ``apply``, ``agg``, and ``transform`` will no longer replace NumPy functions (e.g. ``np.sum``) and built-in functions (e.g. ``min``) with the equivalent pandas implementation; use string aliases (e.g. ``"sum"`` and ``"min"``) if you desire to use the pandas implementation (:issue:`53974`) diff --git a/pandas/tests/frame/methods/test_asfreq.py b/pandas/tests/frame/methods/test_asfreq.py index dd029689e83d3..ffb14a1008b9e 100644 --- a/pandas/tests/frame/methods/test_asfreq.py +++ b/pandas/tests/frame/methods/test_asfreq.py @@ -282,18 +282,11 @@ def test_asfreq_unsupported_freq(self, freq, error_msg): with pytest.raises(ValueError, match=error_msg): df.asfreq(freq=freq) - @pytest.mark.parametrize( - "freq, freq_depr", - [ - ("1YE", "1A"), - ("2YE-MAR", "2A-MAR"), - ], - ) - def test_asfreq_frequency_A_raisees(self, freq, freq_depr): - msg = f"Invalid frequency: {freq_depr[1:]}" + def test_asfreq_frequency_A_raises(self): + msg = "Invalid frequency: 2A" - index = date_range("1/1/2000", periods=4, freq=f"{freq[1:]}") + index = date_range("1/1/2000", periods=4, freq="2ME") df = DataFrame({"s": Series([0.0, 1.0, 2.0, 3.0], index=index)}) with pytest.raises(ValueError, match=msg): - df.asfreq(freq=freq_depr) + df.asfreq(freq="2A")