diff --git a/doc/source/user_guide/timeseries.rst b/doc/source/user_guide/timeseries.rst index 7ffbd9b2d740a..c78921655eb05 100644 --- a/doc/source/user_guide/timeseries.rst +++ b/doc/source/user_guide/timeseries.rst @@ -890,7 +890,7 @@ into ``freq`` keyword arguments. The available date offsets and associated frequ :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" - :class:`~pandas.tseries.offsets.QuarterEnd`, ``'Q'``, "calendar quarter end" + :class:`~pandas.tseries.offsets.QuarterEnd`, ``'QE'``, "calendar quarter end" :class:`~pandas.tseries.offsets.QuarterBegin`, ``'QS'``, "calendar quarter begin" :class:`~pandas.tseries.offsets.BQuarterEnd`, ``'BQ``, "business quarter end" :class:`~pandas.tseries.offsets.BQuarterBegin`, ``'BQS'``, "business quarter begin" @@ -1254,7 +1254,7 @@ frequencies. We will refer to these aliases as *offset aliases*. "SMS", "semi-month start frequency (1st and 15th)" "BMS", "business month start frequency" "CBMS", "custom business month start frequency" - "Q", "quarter end frequency" + "QE", "quarter end frequency" "BQ", "business quarter end frequency" "QS", "quarter start frequency" "BQS", "business quarter start frequency" @@ -1373,18 +1373,18 @@ For some frequencies you can specify an anchoring suffix: "W\-THU", "weekly frequency (Thursdays)" "W\-FRI", "weekly frequency (Fridays)" "W\-SAT", "weekly frequency (Saturdays)" - "(B)Q(S)\-DEC", "quarterly frequency, year ends in December. Same as 'Q'" - "(B)Q(S)\-JAN", "quarterly frequency, year ends in January" - "(B)Q(S)\-FEB", "quarterly frequency, year ends in February" - "(B)Q(S)\-MAR", "quarterly frequency, year ends in March" - "(B)Q(S)\-APR", "quarterly frequency, year ends in April" - "(B)Q(S)\-MAY", "quarterly frequency, year ends in May" - "(B)Q(S)\-JUN", "quarterly frequency, year ends in June" - "(B)Q(S)\-JUL", "quarterly frequency, year ends in July" - "(B)Q(S)\-AUG", "quarterly frequency, year ends in August" - "(B)Q(S)\-SEP", "quarterly frequency, year ends in September" - "(B)Q(S)\-OCT", "quarterly frequency, year ends in October" - "(B)Q(S)\-NOV", "quarterly frequency, year ends in November" + "(B)Q(E)(S)\-DEC", "quarterly frequency, year ends in December. Same as 'QE'" + "(B)Q(E)(S)\-JAN", "quarterly frequency, year ends in January" + "(B)Q(E)(S)\-FEB", "quarterly frequency, year ends in February" + "(B)Q(E)(S)\-MAR", "quarterly frequency, year ends in March" + "(B)Q(E)(S)\-APR", "quarterly frequency, year ends in April" + "(B)Q(E)(S)\-MAY", "quarterly frequency, year ends in May" + "(B)Q(E)(S)\-JUN", "quarterly frequency, year ends in June" + "(B)Q(E)(S)\-JUL", "quarterly frequency, year ends in July" + "(B)Q(E)(S)\-AUG", "quarterly frequency, year ends in August" + "(B)Q(E)(S)\-SEP", "quarterly frequency, year ends in September" + "(B)Q(E)(S)\-OCT", "quarterly frequency, year ends in October" + "(B)Q(E)(S)\-NOV", "quarterly frequency, year ends in November" "(B)Y(S)\-DEC", "annual frequency, anchored end of December. Same as 'Y'" "(B)Y(S)\-JAN", "annual frequency, anchored end of January" "(B)Y(S)\-FEB", "annual frequency, anchored end of February" @@ -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', 'BME', 'BY', 'BQ', and 'W' + frequency offsets except for 'ME', 'Y', 'QE', '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 diff --git a/doc/source/whatsnew/v0.18.0.rst b/doc/source/whatsnew/v0.18.0.rst index 8984109da2a43..569197fe9daf5 100644 --- a/doc/source/whatsnew/v0.18.0.rst +++ b/doc/source/whatsnew/v0.18.0.rst @@ -808,11 +808,19 @@ Upsampling operations take you from a lower frequency to a higher frequency. The performed with the ``Resampler`` objects with :meth:`~Resampler.backfill`, :meth:`~Resampler.ffill`, :meth:`~Resampler.fillna` and :meth:`~Resampler.asfreq` methods. -.. ipython:: python +.. code-block:: ipython - s = pd.Series(np.arange(5, dtype='int64'), + In [89]: s = pd.Series(np.arange(5, dtype='int64'), index=pd.date_range('2010-01-01', periods=5, freq='Q')) - s + + In [90]: s + Out[90]: + 2010-03-31 0 + 2010-06-30 1 + 2010-09-30 2 + 2010-12-31 3 + 2011-03-31 4 + Freq: Q-DEC, Length: 5, dtype: int64 Previously diff --git a/doc/source/whatsnew/v2.2.0.rst b/doc/source/whatsnew/v2.2.0.rst index 3adeec83be964..b74f93942c411 100644 --- a/doc/source/whatsnew/v2.2.0.rst +++ b/doc/source/whatsnew/v2.2.0.rst @@ -210,8 +210,8 @@ Other API changes Deprecations ~~~~~~~~~~~~ -Deprecate alias ``M`` in favour of ``ME`` for offsets -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Deprecate aliases ``M`` and ``Q`` in favour of ``ME`` and ``QE`` for offsets +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The alias ``M`` is deprecated in favour of ``ME`` for offsets, please use ``ME`` for "month end" instead of ``M`` (:issue:`9586`) @@ -232,6 +232,25 @@ For example: pd.date_range('2020-01-01', periods=3, freq='ME') +The alias ``Q`` is deprecated in favour of ``QE`` for offsets, please use ``QE`` for "quarter end" instead of ``Q`` (:issue:`9586`) + +For example: + +*Previous behavior*: + +.. code-block:: ipython + + In [8]: pd.date_range('2020-01-01', periods=3, freq='Q-NOV') + Out[8]: + DatetimeIndex(['2020-02-29', '2020-05-31', '2020-08-31'], + dtype='datetime64[ns]', freq='Q-NOV') + +*Future behavior*: + +.. ipython:: python + + pd.date_range('2020-01-01', periods=3, freq='QE-NOV') + Other Deprecations ^^^^^^^^^^^^^^^^^^ - Changed :meth:`Timedelta.resolution_string` to return ``h``, ``min``, ``s``, ``ms``, ``us``, and ``ns`` instead of ``H``, ``T``, ``S``, ``L``, ``U``, and ``N``, for compatibility with respective deprecations in frequency aliases (:issue:`52536`) diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index 370917df4dca6..ac0b15dc73a8d 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -201,7 +201,19 @@ OFFSET_TO_PERIOD_FREQSTR: dict = { "us": "us", "ns": "ns", "h": "h", - "Q": "Q", + "QE": "Q", + "QE-DEC": "Q-DEC", + "QE-JAN": "Q-JAN", + "QE-FEB": "Q-FEB", + "QE-MAR": "Q-MAR", + "QE-APR": "Q-APR", + "QE-MAY": "Q-MAY", + "QE-JUN": "Q-JUN", + "QE-JUL": "Q-JUL", + "QE-AUG": "Q-AUG", + "QE-SEP": "Q-SEP", + "QE-OCT": "Q-OCT", + "QE-NOV": "Q-NOV", "W": "W", "ME": "M", "Y": "Y", @@ -211,6 +223,19 @@ OFFSET_TO_PERIOD_FREQSTR: dict = { } OFFSET_DEPR_FREQSTR: dict[str, str]= { "M": "ME", + "Q": "QE", + "Q-DEC": "QE-DEC", + "Q-JAN": "QE-JAN", + "Q-FEB": "QE-FEB", + "Q-MAR": "QE-MAR", + "Q-APR": "QE-APR", + "Q-MAY": "QE-MAY", + "Q-JUN": "QE-JUN", + "Q-JUL": "QE-JUL", + "Q-AUG": "QE-AUG", + "Q-SEP": "QE-SEP", + "Q-OCT": "QE-OCT", + "Q-NOV": "QE-NOV", } cdef dict c_OFFSET_TO_PERIOD_FREQSTR = OFFSET_TO_PERIOD_FREQSTR cdef dict c_OFFSET_DEPR_FREQSTR = OFFSET_DEPR_FREQSTR diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index fad41fff6abb6..45188c536d985 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -2767,7 +2767,7 @@ cdef class QuarterEnd(QuarterOffset): Timestamp('2022-03-31 00:00:00') """ _default_starting_month = 3 - _prefix = "Q" + _prefix = "QE" _day_opt = "end" cdef readonly: @@ -4585,7 +4585,7 @@ prefix_mapping = { Second, # 's' Minute, # 'min' Micro, # 'us' - QuarterEnd, # 'Q' + QuarterEnd, # 'QE' QuarterBegin, # 'QS' Milli, # 'ms' Hour, # 'h' @@ -4603,7 +4603,7 @@ opattern = re.compile( _lite_rule_alias = { "W": "W-SUN", - "Q": "Q-DEC", + "QE": "QE-DEC", "Y": "Y-DEC", # YearEnd(month=12), "YS": "YS-JAN", # YearBegin(month=1), @@ -4617,7 +4617,7 @@ _lite_rule_alias = { "ns": "ns", } -_dont_uppercase = {"h", "bh", "cbh", "MS", "ms", "s", "me"} +_dont_uppercase = {"h", "bh", "cbh", "MS", "ms", "s", "me", "qe"} INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}" diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index d90de383adb48..10a3fcc61b5bc 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -284,7 +284,7 @@ class DatetimeProperties(Properties): 2 2 dtype: int32 - >>> quarters_series = pd.Series(pd.date_range("2000-01-01", periods=3, freq="q")) + >>> quarters_series = pd.Series(pd.date_range("2000-01-01", periods=3, freq="QE")) >>> quarters_series 0 2000-03-31 1 2000-06-30 diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 82f3efce1fdd8..d648a0afb8ce4 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", "Y", "Q", "BME", "BY", "BQ", "W"} + end_types = {"ME", "Y", "QE", "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: @@ -2329,7 +2329,7 @@ def _adjust_bin_edges( if self.freq.name in ("BME", "ME", "W") or self.freq.name.split("-")[0] in ( "BQ", "BY", - "Q", + "QE", "Y", "W", ): diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index dd5de83c0cadd..190ce0caceb20 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -1076,7 +1076,7 @@ def test_dt64arr_add_dtlike_raises(self, tz_naive_fixture, box_with_array): # Note: freq here includes both Tick and non-Tick offsets; this is # relevant because historically integer-addition was allowed if we had # a freq. - @pytest.mark.parametrize("freq", ["h", "D", "W", "2ME", "MS", "Q", "B", None]) + @pytest.mark.parametrize("freq", ["h", "D", "W", "2ME", "MS", "QE", "B", None]) @pytest.mark.parametrize("dtype", [None, "uint8"]) def test_dt64arr_addsub_intlike( self, request, dtype, box_with_array, freq, tz_naive_fixture diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 8f49a924a97df..9718381e58fcb 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -30,7 +30,7 @@ # TODO: more freq variants -@pytest.fixture(params=["D", "B", "W", "ME", "Q", "Y"]) +@pytest.fixture(params=["D", "B", "W", "ME", "QE", "Y"]) def freqstr(request): """Fixture returning parametrized frequency in string format.""" return request.param diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index c2d68a79f32d4..a5a6c18900a76 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -746,6 +746,25 @@ def test_iter_zoneinfo_fold(self, tz): assert str(left) == str(right2) assert left.utcoffset() == right2.utcoffset() + @pytest.mark.parametrize( + "freq, freq_depr", + [ + ("2ME", "2M"), + ("2QE", "2Q"), + ("2QE-SEP", "2Q-SEP"), + ], + ) + def test_date_range_frequency_M_Q_deprecated(self, freq, freq_depr): + # GH#9586 + depr_msg = ( + f"'{freq_depr[1:]}' will be deprecated, please use '{freq[1:]}' instead." + ) + + expected = pd.date_range("1/1/2000", periods=4, freq=freq) + with tm.assert_produces_warning(FutureWarning, match=depr_msg): + result = pd.date_range("1/1/2000", periods=4, freq=freq_depr) + tm.assert_index_equal(result, expected) + def test_factorize_sort_without_freq(): dta = DatetimeArray._from_sequence([0, 2, 1]) diff --git a/pandas/tests/frame/methods/test_asfreq.py b/pandas/tests/frame/methods/test_asfreq.py index 2faf5beda9fe6..4fd2e8235f76e 100644 --- a/pandas/tests/frame/methods/test_asfreq.py +++ b/pandas/tests/frame/methods/test_asfreq.py @@ -234,12 +234,23 @@ def test_asfreq_2ME(self, freq, freq_half): result = DataFrame({"s": Series([0.0, 2.0, 4.0], index=index)}) tm.assert_frame_equal(result, expected) - def test_asfreq_frequency_M_deprecated(self): - depr_msg = "'M' will be deprecated, please use 'ME' instead." + @pytest.mark.parametrize( + "freq, freq_depr", + [ + ("2ME", "2M"), + ("2QE", "2Q"), + ("2QE-SEP", "2Q-SEP"), + ], + ) + def test_asfreq_frequency_M_Q_deprecated(self, freq, freq_depr): + # GH#9586 + depr_msg = ( + f"'{freq_depr[1:]}' will be deprecated, please use '{freq[1:]}' instead." + ) - index = date_range("1/1/2000", periods=4, freq="ME") + 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)}) - expected = df.asfreq(freq="5ME") + expected = df.asfreq(freq=freq) with tm.assert_produces_warning(FutureWarning, match=depr_msg): - result = df.asfreq(freq="5M") - tm.assert_frame_equal(result, expected) + result = df.asfreq(freq=freq_depr) + tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/frame/test_nonunique_indexes.py b/pandas/tests/frame/test_nonunique_indexes.py index 4f0d5ad5488c0..8f4b7d27e45f3 100644 --- a/pandas/tests/frame/test_nonunique_indexes.py +++ b/pandas/tests/frame/test_nonunique_indexes.py @@ -29,7 +29,7 @@ def test_setattr_columns_vs_construct_with_columns(self): check(df, expected) def test_setattr_columns_vs_construct_with_columns_datetimeindx(self): - idx = date_range("20130101", periods=4, freq="Q-NOV") + idx = date_range("20130101", periods=4, freq="QE-NOV") df = DataFrame( [[1, 1, 1, 5], [1, 1, 2, 5], [2, 1, 3, 5]], columns=["a", "a", "a", "a"] ) diff --git a/pandas/tests/groupby/test_timegrouper.py b/pandas/tests/groupby/test_timegrouper.py index 31629ba697e33..80860d5192857 100644 --- a/pandas/tests/groupby/test_timegrouper.py +++ b/pandas/tests/groupby/test_timegrouper.py @@ -336,7 +336,7 @@ def test_timegrouper_with_reg_groups(self): ) tm.assert_frame_equal(result, expected) - @pytest.mark.parametrize("freq", ["D", "ME", "Y", "Q-APR"]) + @pytest.mark.parametrize("freq", ["D", "ME", "Y", "QE-APR"]) def test_timegrouper_with_reg_groups_freq(self, freq): # GH 6764 multiple grouping with/without sort df = DataFrame( diff --git a/pandas/tests/indexes/datetimes/methods/test_resolution.py b/pandas/tests/indexes/datetimes/methods/test_resolution.py index 6336fc8277947..5dcf2ad2a5a80 100644 --- a/pandas/tests/indexes/datetimes/methods/test_resolution.py +++ b/pandas/tests/indexes/datetimes/methods/test_resolution.py @@ -10,7 +10,7 @@ "freq,expected", [ ("Y", "day"), - ("Q", "day"), + ("QE", "day"), ("ME", "day"), ("D", "day"), ("h", "hour"), diff --git a/pandas/tests/indexes/datetimes/methods/test_to_period.py b/pandas/tests/indexes/datetimes/methods/test_to_period.py index d95cd6f3a2cc5..c6fbf0f8d4489 100644 --- a/pandas/tests/indexes/datetimes/methods/test_to_period.py +++ b/pandas/tests/indexes/datetimes/methods/test_to_period.py @@ -54,7 +54,7 @@ def test_to_period_quarterly(self, month): def test_to_period_quarterlyish(self, off): rng = date_range("01-Jan-2012", periods=8, freq=off) prng = rng.to_period() - assert prng.freq == "Q-DEC" + assert prng.freq == "QE-DEC" @pytest.mark.parametrize("off", ["BY", "YS", "BYS"]) def test_to_period_annualish(self, off): @@ -89,6 +89,23 @@ def test_dti_to_period_2monthish(self, freq_offset, freq_period): tm.assert_index_equal(pi, period_range("2020-01", "2020-05", freq=freq_period)) + @pytest.mark.parametrize( + "freq, freq_depr", + [ + ("2ME", "2M"), + ("2QE", "2Q"), + ("2QE-SEP", "2Q-SEP"), + ], + ) + def test_to_period_freq_deprecated(self, freq, freq_depr): + # GH#9586 + msg = f"'{freq_depr[1:]}' will be 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): + assert 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_constructors.py b/pandas/tests/indexes/datetimes/test_constructors.py index 035d74756c2e8..08ec11c87b623 100644 --- a/pandas/tests/indexes/datetimes/test_constructors.py +++ b/pandas/tests/indexes/datetimes/test_constructors.py @@ -1101,7 +1101,8 @@ def test_constructor_int64_nocopy(self): assert (index.asi8[50:100] != -1).all() @pytest.mark.parametrize( - "freq", ["ME", "Q", "Y", "D", "B", "bh", "min", "s", "ms", "us", "h", "ns", "C"] + "freq", + ["ME", "QE", "Y", "D", "B", "bh", "min", "s", "ms", "us", "h", "ns", "C"], ) def test_from_freq_recreate_from_data(self, freq): org = date_range(start="2001/02/01 09:00", freq=freq, periods=1) diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index ed2c2b473c2df..e902c8236894e 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -1018,7 +1018,7 @@ def test_3(self): def test_precision_finer_than_offset(self): # GH#9907 result1 = date_range( - start="2015-04-15 00:00:03", end="2016-04-22 00:00:00", freq="Q" + start="2015-04-15 00:00:03", end="2016-04-22 00:00:00", freq="QE" ) result2 = date_range( start="2015-04-15 00:00:03", end="2015-06-22 00:00:04", freq="W" @@ -1042,7 +1042,7 @@ def test_precision_finer_than_offset(self): "2015-06-21 00:00:03", ] expected1 = DatetimeIndex( - expected1_list, dtype="datetime64[ns]", freq="Q-DEC", tz=None + expected1_list, dtype="datetime64[ns]", freq="QE-DEC", tz=None ) expected2 = DatetimeIndex( expected2_list, dtype="datetime64[ns]", freq="W-SUN", tz=None diff --git a/pandas/tests/indexes/datetimes/test_setops.py b/pandas/tests/indexes/datetimes/test_setops.py index 4f86e3c721aab..62b5e72d5e025 100644 --- a/pandas/tests/indexes/datetimes/test_setops.py +++ b/pandas/tests/indexes/datetimes/test_setops.py @@ -364,8 +364,8 @@ def test_difference_freq(self, sort): tm.assert_attr_equal("freq", idx_diff, expected) def test_datetimeindex_diff(self, sort): - dti1 = date_range(freq="Q-JAN", start=datetime(1997, 12, 31), periods=100) - dti2 = date_range(freq="Q-JAN", start=datetime(1997, 12, 31), periods=98) + dti1 = date_range(freq="QE-JAN", start=datetime(1997, 12, 31), periods=100) + dti2 = date_range(freq="QE-JAN", start=datetime(1997, 12, 31), periods=98) assert len(dti1.difference(dti2, sort)) == 2 @pytest.mark.parametrize("tz", [None, "Asia/Tokyo", "US/Eastern"]) @@ -414,7 +414,7 @@ def test_intersection_non_tick_no_fastpath(self): "2019-12-31", "2020-03-31", ], - freq="Q-DEC", + freq="QE-DEC", ) result = dti[::2].intersection(dti[1::2]) expected = dti[:0] diff --git a/pandas/tests/indexes/period/test_period.py b/pandas/tests/indexes/period/test_period.py index 1bb8d66332cd0..84b8c55f3f0fa 100644 --- a/pandas/tests/indexes/period/test_period.py +++ b/pandas/tests/indexes/period/test_period.py @@ -307,6 +307,14 @@ def test_a_deprecated_from_time_series(self, freq): series = Series(1, index=index) assert isinstance(series, Series) + @pytest.mark.parametrize("freq_depr", ["2ME", "2QE"]) + def test_period_index_frequency_error_message(self, freq_depr): + # GH#9586 + msg = f"Invalid frequency: {freq_depr}" + + with pytest.raises(ValueError, match=msg): + period_range("2020-01", "2020-05", freq=freq_depr) + def test_maybe_convert_timedelta(): pi = PeriodIndex(["2000", "2001"], freq="D") diff --git a/pandas/tests/indexes/period/test_period_range.py b/pandas/tests/indexes/period/test_period_range.py index bee8a1282d08b..e908cda449ee6 100644 --- a/pandas/tests/indexes/period/test_period_range.py +++ b/pandas/tests/indexes/period/test_period_range.py @@ -20,33 +20,41 @@ def test_required_arguments(self): with pytest.raises(ValueError, match=msg): period_range("2011-1-1", "2012-1-1", "B") - @pytest.mark.parametrize("freq", ["D", "W", "Q", "Y"]) - def test_construction_from_string(self, freq): + @pytest.mark.parametrize( + "freq_offset, freq_period", + [ + ("D", "D"), + ("W", "W"), + ("QE", "Q"), + ("Y", "Y"), + ], + ) + def test_construction_from_string(self, freq_offset, freq_period): # non-empty expected = date_range( - start="2017-01-01", periods=5, freq=freq, name="foo" + start="2017-01-01", periods=5, freq=freq_offset, name="foo" ).to_period() start, end = str(expected[0]), str(expected[-1]) - result = period_range(start=start, end=end, freq=freq, name="foo") + result = period_range(start=start, end=end, freq=freq_period, name="foo") tm.assert_index_equal(result, expected) - result = period_range(start=start, periods=5, freq=freq, name="foo") + result = period_range(start=start, periods=5, freq=freq_period, name="foo") tm.assert_index_equal(result, expected) - result = period_range(end=end, periods=5, freq=freq, name="foo") + result = period_range(end=end, periods=5, freq=freq_period, name="foo") tm.assert_index_equal(result, expected) # empty - expected = PeriodIndex([], freq=freq, name="foo") + expected = PeriodIndex([], freq=freq_period, name="foo") - result = period_range(start=start, periods=0, freq=freq, name="foo") + result = period_range(start=start, periods=0, freq=freq_period, name="foo") tm.assert_index_equal(result, expected) - result = period_range(end=end, periods=0, freq=freq, name="foo") + result = period_range(end=end, periods=0, freq=freq_period, name="foo") tm.assert_index_equal(result, expected) - result = period_range(start=end, end=start, freq=freq, name="foo") + result = period_range(start=end, end=start, freq=freq_period, name="foo") tm.assert_index_equal(result, expected) def test_construction_from_string_monthly(self): @@ -89,7 +97,7 @@ def test_construction_from_period(self): # downsampling start, end = Period("2017-1", freq="M"), Period("2019-12", freq="M") expected = date_range( - start="2017-01-31", end="2019-12-31", freq="Q", name="foo" + start="2017-01-31", end="2019-12-31", freq="QE", name="foo" ).to_period() result = period_range(start=start, end=end, freq="Q", name="foo") tm.assert_index_equal(result, expected) diff --git a/pandas/tests/io/json/test_json_table_schema.py b/pandas/tests/io/json/test_json_table_schema.py index ddab3887db810..507e99a4d6490 100644 --- a/pandas/tests/io/json/test_json_table_schema.py +++ b/pandas/tests/io/json/test_json_table_schema.py @@ -389,7 +389,7 @@ def test_to_json_period_index(self): result["schema"].pop("pandas_version") fields = [ - {"freq": "Q-JAN", "name": "index", "type": "datetime"}, + {"freq": "QE-JAN", "name": "index", "type": "datetime"}, {"name": "values", "type": "integer"}, ] @@ -846,7 +846,7 @@ def test_read_json_orient_table_old_schema_version(self): result = pd.read_json(StringIO(df_json), orient="table") tm.assert_frame_equal(expected, result) - @pytest.mark.parametrize("freq", ["M", "2M"]) + @pytest.mark.parametrize("freq", ["M", "2M", "Q", "2Q"]) def test_read_json_table_orient_period_depr_freq(self, freq, recwarn): # GH#9586 df = DataFrame( diff --git a/pandas/tests/plotting/test_converter.py b/pandas/tests/plotting/test_converter.py index 7d574b86cef36..509e0ea5c482e 100644 --- a/pandas/tests/plotting/test_converter.py +++ b/pandas/tests/plotting/test_converter.py @@ -392,7 +392,7 @@ def test_quarterly_finder(year_span): pytest.skip("the quarterly finder is only invoked if the span is >= 45") nyears = span / 4 (min_anndef, maj_anndef) = converter._get_default_annual_spacing(nyears) - result = converter._quarterly_finder(vmin, vmax, to_offset("Q")) + result = converter._quarterly_finder(vmin, vmax, to_offset("QE")) quarters = PeriodIndex( arrays.PeriodArray(np.array([x[0] for x in result]), dtype="period[Q]") ) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index a384fd9cdc8f2..42f1a30983414 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -125,7 +125,7 @@ def test_tsplot_period(self, freq): _check_plot_works(ser.plot, ax=ax) @pytest.mark.parametrize( - "freq", ["s", "min", "h", "D", "W", "ME", "Q-DEC", "Y", "1B30Min"] + "freq", ["s", "min", "h", "D", "W", "ME", "QE-DEC", "Y", "1B30Min"] ) def test_tsplot_datetime(self, freq): idx = date_range("12/31/1999", freq=freq, periods=100) @@ -204,14 +204,14 @@ def test_line_plot_period_mlt_series(self, frqncy): _check_plot_works(s.plot, s.index.freq.rule_code) @pytest.mark.parametrize( - "freq", ["s", "min", "h", "D", "W", "ME", "Q-DEC", "Y", "1B30Min"] + "freq", ["s", "min", "h", "D", "W", "ME", "QE-DEC", "Y", "1B30Min"] ) def test_line_plot_datetime_series(self, freq): idx = date_range("12/31/1999", freq=freq, periods=100) ser = Series(np.random.default_rng(2).standard_normal(len(idx)), idx) _check_plot_works(ser.plot, ser.index.freq.rule_code) - @pytest.mark.parametrize("freq", ["s", "min", "h", "D", "W", "ME", "Q", "Y"]) + @pytest.mark.parametrize("freq", ["s", "min", "h", "D", "W", "ME", "QE", "Y"]) def test_line_plot_period_frame(self, freq): idx = date_range("12/31/1999", freq=freq, periods=100) df = DataFrame( @@ -240,7 +240,7 @@ def test_line_plot_period_mlt_frame(self, frqncy): @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") @pytest.mark.parametrize( - "freq", ["s", "min", "h", "D", "W", "ME", "Q-DEC", "Y", "1B30Min"] + "freq", ["s", "min", "h", "D", "W", "ME", "QE-DEC", "Y", "1B30Min"] ) def test_line_plot_datetime_frame(self, freq): idx = date_range("12/31/1999", freq=freq, periods=100) @@ -254,7 +254,7 @@ def test_line_plot_datetime_frame(self, freq): _check_plot_works(df.plot, freq) @pytest.mark.parametrize( - "freq", ["s", "min", "h", "D", "W", "ME", "Q-DEC", "Y", "1B30Min"] + "freq", ["s", "min", "h", "D", "W", "ME", "QE-DEC", "Y", "1B30Min"] ) def test_line_plot_inferred_freq(self, freq): idx = date_range("12/31/1999", freq=freq, periods=100) @@ -439,7 +439,7 @@ def test_get_finder(self): assert conv.get_finder(to_offset("B")) == conv._daily_finder assert conv.get_finder(to_offset("D")) == conv._daily_finder assert conv.get_finder(to_offset("ME")) == conv._monthly_finder - assert conv.get_finder(to_offset("Q")) == conv._quarterly_finder + assert conv.get_finder(to_offset("QE")) == conv._quarterly_finder assert conv.get_finder(to_offset("Y")) == conv._annual_finder assert conv.get_finder(to_offset("W")) == conv._daily_finder diff --git a/pandas/tests/resample/test_datetime_index.py b/pandas/tests/resample/test_datetime_index.py index 53bd4b056cfbd..b53b1304374d7 100644 --- a/pandas/tests/resample/test_datetime_index.py +++ b/pandas/tests/resample/test_datetime_index.py @@ -644,7 +644,7 @@ def test_resample_dup_index(): df.iloc[3, :] = np.nan warning_msg = "DataFrame.resample with axis=1 is deprecated." with tm.assert_produces_warning(FutureWarning, match=warning_msg): - result = df.resample("Q", axis=1).mean() + result = df.resample("QE", axis=1).mean() msg = "DataFrame.groupby with axis=1 is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg): @@ -1149,19 +1149,19 @@ def test_resample_anchored_intraday(simple_date_range_series, unit): rng = date_range("1/1/2012", "4/1/2012", freq="100min").as_unit(unit) df = DataFrame(rng.month, index=rng) - result = df.resample("Q").mean() - expected = df.resample("Q", kind="period").mean().to_timestamp(how="end") + result = df.resample("QE").mean() + expected = df.resample("QE", kind="period").mean().to_timestamp(how="end") expected.index += Timedelta(1, "ns") - Timedelta(1, "D") - expected.index._data.freq = "Q" + expected.index._data.freq = "QE" expected.index._freq = lib.no_default expected.index = expected.index.as_unit(unit) tm.assert_frame_equal(result, expected) - result = df.resample("Q", closed="left").mean() - expected = df.shift(1, freq="D").resample("Q", kind="period", closed="left").mean() + result = df.resample("QE", closed="left").mean() + expected = df.shift(1, freq="D").resample("QE", kind="period", closed="left").mean() expected = expected.to_timestamp(how="end") expected.index += Timedelta(1, "ns") - Timedelta(1, "D") - expected.index._data.freq = "Q" + expected.index._data.freq = "QE" expected.index._freq = lib.no_default expected.index = expected.index.as_unit(unit) tm.assert_frame_equal(result, expected) @@ -1871,11 +1871,11 @@ def test_resample_apply_product(duplicates, unit): msg = "using DatetimeIndexResampler.prod" with tm.assert_produces_warning(FutureWarning, match=msg): - result = df.resample("Q").apply(np.prod) + result = df.resample("QE").apply(np.prod) expected = DataFrame( np.array([[0, 24], [60, 210], [336, 720], [990, 1716]], dtype=np.int64), index=DatetimeIndex( - ["2012-03-31", "2012-06-30", "2012-09-30", "2012-12-31"], freq="Q-DEC" + ["2012-03-31", "2012-06-30", "2012-09-30", "2012-12-31"], freq="QE-DEC" ).as_unit(unit), columns=df.columns, ) @@ -1937,10 +1937,10 @@ def test_resample_aggregate_functions_min_count(func, unit): # GH#37768 index = date_range(start="2020", freq="ME", periods=3).as_unit(unit) ser = Series([1, np.nan, np.nan], index) - result = getattr(ser.resample("Q"), func)(min_count=2) + result = getattr(ser.resample("QE"), func)(min_count=2) expected = Series( [np.nan], - index=DatetimeIndex(["2020-03-31"], freq="Q-DEC").as_unit(unit), + index=DatetimeIndex(["2020-03-31"], freq="QE-DEC").as_unit(unit), ) tm.assert_series_equal(result, expected) @@ -2010,13 +2010,22 @@ def test_resample_empty_series_with_tz(): tm.assert_series_equal(result, expected) -def test_resample_M_deprecated(): - depr_msg = "'M' will be deprecated, please use 'ME' instead." +@pytest.mark.parametrize( + "freq, freq_depr", + [ + ("2ME", "2M"), + ("2QE", "2Q"), + ("2QE-SEP", "2Q-SEP"), + ], +) +def test_resample_M_Q_deprecated(freq, freq_depr): + # GH#9586 + depr_msg = f"'{freq_depr[1:]}' will be deprecated, please use '{freq[1:]}' instead." s = Series(range(10), index=date_range("20130101", freq="d", periods=10)) - expected = s.resample("2ME").mean() + expected = s.resample(freq).mean() with tm.assert_produces_warning(FutureWarning, match=depr_msg): - result = s.resample("2M").mean() + result = s.resample(freq_depr).mean() tm.assert_series_equal(result, expected) diff --git a/pandas/tests/resample/test_period_index.py b/pandas/tests/resample/test_period_index.py index 6ad09f12525b4..e768d9266ab89 100644 --- a/pandas/tests/resample/test_period_index.py +++ b/pandas/tests/resample/test_period_index.py @@ -104,7 +104,7 @@ def test_selection(self, index, freq, kind, kwargs): @pytest.mark.parametrize("meth", ["ffill", "bfill"]) @pytest.mark.parametrize("conv", ["start", "end"]) @pytest.mark.parametrize( - ("offset", "period"), [("D", "D"), ("B", "B"), ("ME", "M")] + ("offset", "period"), [("D", "D"), ("B", "B"), ("ME", "M"), ("QE", "Q")] ) def test_annual_upsample_cases( self, offset, period, conv, meth, month, simple_period_range_series @@ -134,7 +134,7 @@ def test_basic_downsample(self, simple_period_range_series): "rule,expected_error_msg", [ ("y-dec", ""), - ("q-mar", ""), + ("qe-mar", ""), ("M", ""), ("w-thu", ""), ], @@ -367,7 +367,7 @@ def test_fill_method_and_how_upsample(self): # GH2073 s = Series( np.arange(9, dtype="int64"), - index=date_range("2010-01-01", periods=9, freq="Q"), + index=date_range("2010-01-01", periods=9, freq="QE"), ) last = s.resample("ME").ffill() both = s.resample("ME").ffill().resample("ME").last().astype("int64") @@ -647,7 +647,7 @@ def test_monthly_convention_span(self): tm.assert_series_equal(result, expected) @pytest.mark.parametrize( - "from_freq, to_freq", [("D", "ME"), ("Q", "Y"), ("ME", "Q"), ("D", "W")] + "from_freq, to_freq", [("D", "ME"), ("QE", "Y"), ("ME", "QE"), ("D", "W")] ) def test_default_right_closed_label(self, from_freq, to_freq): idx = date_range(start="8/15/2012", periods=100, freq=from_freq) @@ -660,7 +660,7 @@ def test_default_right_closed_label(self, from_freq, to_freq): @pytest.mark.parametrize( "from_freq, to_freq", - [("D", "MS"), ("Q", "YS"), ("ME", "QS"), ("h", "D"), ("min", "h")], + [("D", "MS"), ("QE", "YS"), ("ME", "QS"), ("h", "D"), ("min", "h")], ) def test_default_left_closed_label(self, from_freq, to_freq): idx = date_range(start="8/15/2012", periods=100, freq=from_freq) @@ -931,9 +931,11 @@ def test_resample_t_l_deprecated(self): tm.assert_series_equal(result, expected) -def test_resample_frequency_ME_error_message(series_and_frame): - msg = "Invalid frequency: 2ME" +@pytest.mark.parametrize("freq_depr", ["2ME", "2QE", "2QE-FEB"]) +def test_resample_frequency_ME_QE_error_message(series_and_frame, freq_depr): + # GH#9586 + msg = f"Invalid frequency: {freq_depr}" obj = series_and_frame with pytest.raises(ValueError, match=msg): - obj.resample("2ME") + obj.resample(freq_depr) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index dc2938ec345f3..8cc3ace52a4d4 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -68,15 +68,15 @@ def test_construction(self): assert i1 == i2 assert i1 == i3 + # GH#54105 - Period can be confusingly instantiated with lowercase freq + # TODO: raise in the future an error when passing lowercase freq i4 = Period("2005", freq="M") assert i1 != i4 i1 = Period.now(freq="Q") i2 = Period(datetime.now(), freq="Q") - i3 = Period.now("q") assert i1 == i2 - assert i1 == i3 # Pass in freq as a keyword argument sometimes as a test for # https://github.com/pandas-dev/pandas/issues/53369 diff --git a/pandas/tests/tseries/frequencies/test_inference.py b/pandas/tests/tseries/frequencies/test_inference.py index 867363e1a03bc..313e6ba592fe0 100644 --- a/pandas/tests/tseries/frequencies/test_inference.py +++ b/pandas/tests/tseries/frequencies/test_inference.py @@ -51,7 +51,7 @@ def base_delta_code_pair(request): freqs = ( - [f"Q-{month}" for month in MONTHS] + [f"QE-{month}" for month in MONTHS] + [f"{annual}-{month}" for annual in ["Y", "BY"] for month in MONTHS] + ["ME", "BME", "BMS"] + [f"WOM-{count}{day}" for count in range(1, 5) for day in DAYS] @@ -67,28 +67,28 @@ def test_infer_freq_range(periods, freq): gen = date_range("1/1/2000", periods=periods, freq=freq) index = DatetimeIndex(gen.values) - if not freq.startswith("Q-"): + if not freq.startswith("QE-"): assert frequencies.infer_freq(index) == gen.freqstr else: inf_freq = frequencies.infer_freq(index) - is_dec_range = inf_freq == "Q-DEC" and gen.freqstr in ( - "Q", - "Q-DEC", - "Q-SEP", - "Q-JUN", - "Q-MAR", + is_dec_range = inf_freq == "QE-DEC" and gen.freqstr in ( + "QE", + "QE-DEC", + "QE-SEP", + "QE-JUN", + "QE-MAR", ) - is_nov_range = inf_freq == "Q-NOV" and gen.freqstr in ( - "Q-NOV", - "Q-AUG", - "Q-MAY", - "Q-FEB", + is_nov_range = inf_freq == "QE-NOV" and gen.freqstr in ( + "QE-NOV", + "QE-AUG", + "QE-MAY", + "QE-FEB", ) - is_oct_range = inf_freq == "Q-OCT" and gen.freqstr in ( - "Q-OCT", - "Q-JUL", - "Q-APR", - "Q-JAN", + is_oct_range = inf_freq == "QE-OCT" and gen.freqstr in ( + "QE-OCT", + "QE-JUL", + "QE-APR", + "QE-JAN", ) assert is_dec_range or is_nov_range or is_oct_range @@ -202,7 +202,7 @@ def test_infer_freq_custom(base_delta_code_pair, constructor): @pytest.mark.parametrize( - "freq,expected", [("Q", "Q-DEC"), ("Q-NOV", "Q-NOV"), ("Q-OCT", "Q-OCT")] + "freq,expected", [("Q", "QE-DEC"), ("Q-NOV", "QE-NOV"), ("Q-OCT", "QE-OCT")] ) def test_infer_freq_index(freq, expected): rng = period_range("1959Q2", "2009Q3", freq=freq) @@ -216,7 +216,7 @@ def test_infer_freq_index(freq, expected): list( { "YS-JAN": ["2009-01-01", "2010-01-01", "2011-01-01", "2012-01-01"], - "Q-OCT": ["2009-01-31", "2009-04-30", "2009-07-31", "2009-10-31"], + "QE-OCT": ["2009-01-31", "2009-04-30", "2009-07-31", "2009-10-31"], "ME": ["2010-11-30", "2010-12-31", "2011-01-31", "2011-02-28"], "W-SAT": ["2010-12-25", "2011-01-01", "2011-01-08", "2011-01-15"], "D": ["2011-01-01", "2011-01-02", "2011-01-03", "2011-01-04"], @@ -477,9 +477,9 @@ def test_series_datetime_index(freq): "W@FRI", "W@SAT", "W@SUN", - "Q@JAN", - "Q@FEB", - "Q@MAR", + "QE@JAN", + "QE@FEB", + "QE@MAR", "Y@JAN", "Y@FEB", "Y@MAR", diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 31e118ecaf5a7..c46c0ddfdc201 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -838,7 +838,7 @@ def test_rule_code(self): "NOV", "DEC", ] - base_lst = ["Y", "YS", "BY", "BYS", "Q", "QS", "BQ", "BQS"] + base_lst = ["Y", "YS", "BY", "BYS", "QE", "QS", "BQ", "BQS"] for base in base_lst: for v in suffix_lst: alias = "-".join([base, v]) @@ -857,7 +857,7 @@ def test_freq_offsets(): class TestReprNames: def test_str_for_named_is_name(self): # look at all the amazing combinations! - month_prefixes = ["Y", "YS", "BY", "BYS", "Q", "BQ", "BQS", "QS"] + month_prefixes = ["Y", "YS", "BY", "BYS", "QE", "BQ", "BQS", "QS"] names = [ prefix + "-" + month for prefix in month_prefixes @@ -1120,7 +1120,7 @@ def test_dateoffset_operations_on_dataframes(): def test_is_yqm_start_end(): freq_m = to_offset("ME") bm = to_offset("BME") - qfeb = to_offset("Q-FEB") + qfeb = to_offset("QE-FEB") qsfeb = to_offset("QS-FEB") bq = to_offset("BQ") bqs_apr = to_offset("BQS-APR") diff --git a/pandas/tests/tslibs/test_to_offset.py b/pandas/tests/tslibs/test_to_offset.py index 82b0c78002972..3fb205f21a857 100644 --- a/pandas/tests/tslibs/test_to_offset.py +++ b/pandas/tests/tslibs/test_to_offset.py @@ -158,9 +158,9 @@ def test_to_offset_pd_timedelta(kwargs, expected): [ ("W", offsets.Week(weekday=6)), ("W-SUN", offsets.Week(weekday=6)), - ("Q", offsets.QuarterEnd(startingMonth=12)), - ("Q-DEC", offsets.QuarterEnd(startingMonth=12)), - ("Q-MAY", offsets.QuarterEnd(startingMonth=5)), + ("QE", offsets.QuarterEnd(startingMonth=12)), + ("QE-DEC", offsets.QuarterEnd(startingMonth=12)), + ("QE-MAY", offsets.QuarterEnd(startingMonth=5)), ("SM", offsets.SemiMonthEnd(day_of_month=15)), ("SM-15", offsets.SemiMonthEnd(day_of_month=15)), ("SM-1", offsets.SemiMonthEnd(day_of_month=1)), diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 67958a03c3969..7a6083f2246b1 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -359,7 +359,7 @@ def _get_quarterly_rule(self) -> str | None: if pos_check is None: return None else: - return {"cs": "QS", "bs": "BQS", "ce": "Q", "be": "BQ"}.get(pos_check) + return {"cs": "QS", "bs": "BQS", "ce": "QE", "be": "BQ"}.get(pos_check) def _get_monthly_rule(self) -> str | None: if len(self.mdiffs) > 1: