Skip to content

Commit a98be06

Browse files
authored
DEPR offsets: rename 'M' to 'ME' (#52064)
* Frequency: raise warnings when using ‘M’ frequency * Frequency: raise warnings when using ‘M’ frequency II * remove is_period and change str representation for freq in Period [skip ci] * remove is_period and fix some tests [skip ci] * fix some tests * fix some tests II * fix tests in pandas/tests/indexes/period/ [skip ci] * fix tests in pandas/tests/indexes/period/ and correct timedeltas.pyx * update frequencies.py, resample.py, and fix some tests * modify pandas/tseries/frequencies.py * fix tests * fix tests II * fix tests III * rename 'M' to 'ME' in docs * rename 'M' to 'ME' in docs II * rename 'M' to 'ME' in docs III * rename 'M' to 'ME' in docs IV * rename 'M' to 'ME' in docs V * add is_period to to_offset I * add is_period to to_offset II * correct the definition of period_array(…) and fix 19 tests * add is_period to _parse_dtype_strict() and fix tests * add constant OFFSET_TO_PERIOD_FREQSTR to period.pyx and fix tests * correct definitions of extract_ordinals() and _round(), fix tests * add replacement ME to M in _require_matching_freq, _parsed_string_to_bounds, and fix tests * add the constant PERIOD_TO_OFFSET_FREQSTR to period.pyx, correct definition of _resolution_obj and fix tests * fix tests * add the conversion ME to M to _from_datetime64, period_index, raise_on_incompatible and fix tests * fix some tests with resample * correct definitions of to_period, freqstr and get_period_alias, fix tests for plotting * correct pre-commit failures * add key from Grouper to the constructor of TimeGrouper and fix tests * add to asfreq() from resampler the conversion ME to M, fix tests * fix tests for for PeriodIndex and base tests for resample * correct the constructor of TimeGrouper and fix tests for resample and plotting * correct the definition of use_dynamic_x() and fix tests for plotting * correct the definition of the method use_dynamic_x, fix tests * correct the definition of the asfreq for PeriodArray, _get_period_alias, and fix tests * correct documentation, fix tests * correct docs: rename ME to M for periods * add pytest.mark.xfail to test_to_timestamp_quarterly_bug * correct mypy error attr-defined * correct the definition of variables which convert M/ME to ME/M in dtypes.pyx, declare to_offset in offsets.pyi, fix mypy errors * created the c version for dicts which convert M/ME to ME/M and fix mypy errors * fix doc build error in 09_timeseries.rst and mypy error * correct the constructor of Period, fix mypy errors * replace in _attrname_to_abbrevs ME with M and correct the constructor of Period * add conversion ME/M to Period constructor, add conversion M/ME to maybe_resample and reverse changes in _attrname_to_abbrevs * correct dict “time rules”, correct the definition of _parsed_string_to_bounds, remove is_period from definition _parse_weekly_str and _parse_dtype_strict * remove the argument is_period from _parse_dtype_strict * add to is_subperiod, is_superperiod and _is_monthly both M and ME, correct definitions of _downsample and _maybe_cast_slice_bound * add dict ME to M to the definition of freqstr, constructor of Period and remove pytest.mark.xfail from test_round_trip_current * refactor freqstr, extract_ordinals, and _require_matching_freq for Period, asfreq for resample and _parsed_string_to_bounds for datetimes * refactor _resolution_obj in dtypes.pyx and freqstr in /indexes/datetimelike.py * define a new function freq_to_period_freqstr in dtypes to convert ME to M * refactor use_dynamic_x for plotting and to_period in arrays/datetimes.py * refactor def _check_plot_works in plotting and test_to_period in class TestDatetimeArray * refactor name method of PeriodDtype, refactor __arrow_array__ and add test for ValueError in test_period.py * in PeriodArray refactor _from_datetime64 and remove redundant if in asfreq, add test for ValueError in test_period_index.py and ignore mypy error * correct def _resolution_obj in DatetimeLikeArrayMixin, refactor def freqstr in PeriodArray and add tests ValueError for ME * correct def _resolution_obj in DatetimeLikeArrayMixin and def to_offset, refactor def freqstr in PeriodArray and add tests for ‘ValueError’ and 'UserWarning' * add tests for 'UserWarning' * refactor methods to_period in DatetimeArray, _from_datetime64 in PeriodArray, fix test in plotting * add freq_to_offset_freqstr to convert M to ME, refactor _resolution_obj, add tests for ‘ValueError’ and 'UserWarning' * fix pre-commit failures * correct the definition of to_period in DatetimeArray, refactor _check_plot_works, fix test_asfreq_2M * correct definitions of _resolution_obj in dtypes.pyx and in DatetimeLikeArrayMixin, _attrname_to_abbrevs and fix test_get_attrname_from_abbrev * correct def asfreq in PeriodArray, remove unused function freq_to_offset_freqstr, fix tests * roll back in test_fillna_period dtype Period[M] with capital P * refactor the function raise_on_incompatible * fix mypy error in pandas/core/arrays/period.py * fix ruff error in pandas/tests/arrays/period/test_constructors.py * remove ME from definitions of is_monthly, is_subperiod, correct _maybe_coerce_freq and test_period_ordinal_start_values * fix test_dti_to_period_2monthish * update whatsnew/v2.1.0.rst * add an example for old/new behavior in whatsnew/v2.1.0.rst * corrected typo * replace name of section Deprecations with Other Deprecations * remove ME form is_superperiod, refactored tests * correct a test * move some tests to a new place * correct def asfreq for resampling, refactor asfreq for Period, fix tests * correct tests * correct def _shift_with_freq and fix test for shift * correct docs for asfreq in PeriodArray * correct def _shift_with_freq * add ‘me’ to _dont_uppercase, correct _require_matching_freq, fix tests * minor corrections * correct whatsnew * correct an example in user_guide/reshaping.rst * fix tests for plotting * correct tests for plotting * remove from OFFSET_TO_PERIOD_FREQSTR deprecated freqstr, fix tests
1 parent 61a6335 commit a98be06

File tree

96 files changed

+726
-397
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+726
-397
lines changed

doc/source/getting_started/intro_tutorials/09_timeseries.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ Aggregate the current hourly time series values to the monthly maximum value in
295295

296296
.. ipython:: python
297297
298-
monthly_max = no_2.resample("M").max()
298+
monthly_max = no_2.resample("ME").max()
299299
monthly_max
300300
301301
A very powerful method on time series data with a datetime index, is the

doc/source/user_guide/cookbook.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ To create year and month cross tabulation:
771771
772772
df = pd.DataFrame(
773773
{"value": np.random.randn(36)},
774-
index=pd.date_range("2011-01-01", freq="M", periods=36),
774+
index=pd.date_range("2011-01-01", freq="ME", periods=36),
775775
)
776776
777777
pd.pivot_table(

doc/source/user_guide/groupby.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -1416,7 +1416,7 @@ Groupby a specific column with the desired frequency. This is like resampling.
14161416

14171417
.. ipython:: python
14181418
1419-
df.groupby([pd.Grouper(freq="1M", key="Date"), "Buyer"])[["Quantity"]].sum()
1419+
df.groupby([pd.Grouper(freq="1ME", key="Date"), "Buyer"])[["Quantity"]].sum()
14201420
14211421
When ``freq`` is specified, the object returned by ``pd.Grouper`` will be an
14221422
instance of ``pandas.api.typing.TimeGrouper``. You have an ambiguous specification
@@ -1426,9 +1426,9 @@ in that you have a named index and a column that could be potential groupers.
14261426
14271427
df = df.set_index("Date")
14281428
df["Date"] = df.index + pd.offsets.MonthEnd(2)
1429-
df.groupby([pd.Grouper(freq="6M", key="Date"), "Buyer"])[["Quantity"]].sum()
1429+
df.groupby([pd.Grouper(freq="6ME", key="Date"), "Buyer"])[["Quantity"]].sum()
14301430
1431-
df.groupby([pd.Grouper(freq="6M", level="Date"), "Buyer"])[["Quantity"]].sum()
1431+
df.groupby([pd.Grouper(freq="6ME", level="Date"), "Buyer"])[["Quantity"]].sum()
14321432
14331433
14341434
Taking the first rows of each group

doc/source/user_guide/reshaping.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ Also, you can use :class:`Grouper` for ``index`` and ``columns`` keywords. For d
136136

137137
.. ipython:: python
138138
139-
pd.pivot_table(df, values="D", index=pd.Grouper(freq="M", key="F"), columns="C")
139+
pd.pivot_table(df, values="D", index=pd.Grouper(freq="ME", key="F"), columns="C")
140140
141141
.. _reshaping.pivot.margins:
142142

doc/source/user_guide/timeseries.rst

+9-9
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ data however will be stored as ``object`` data.
107107
108108
pd.Series(pd.period_range("1/1/2011", freq="M", periods=3))
109109
pd.Series([pd.DateOffset(1), pd.DateOffset(2)])
110-
pd.Series(pd.date_range("1/1/2011", freq="M", periods=3))
110+
pd.Series(pd.date_range("1/1/2011", freq="ME", periods=3))
111111
112112
Lastly, pandas represents null date times, time deltas, and time spans as ``NaT`` which
113113
is useful for representing missing or null date like values and behaves similar
@@ -450,7 +450,7 @@ variety of :ref:`frequency aliases <timeseries.offset_aliases>`:
450450

451451
.. ipython:: python
452452
453-
pd.date_range(start, periods=1000, freq="M")
453+
pd.date_range(start, periods=1000, freq="ME")
454454
455455
pd.bdate_range(start, periods=250, freq="BQS")
456456
@@ -882,7 +882,7 @@ into ``freq`` keyword arguments. The available date offsets and associated frequ
882882
:class:`~pandas.tseries.offsets.Week`, ``'W'``, "one week, optionally anchored on a day of the week"
883883
:class:`~pandas.tseries.offsets.WeekOfMonth`, ``'WOM'``, "the x-th day of the y-th week of each month"
884884
:class:`~pandas.tseries.offsets.LastWeekOfMonth`, ``'LWOM'``, "the x-th day of the last week of each month"
885-
:class:`~pandas.tseries.offsets.MonthEnd`, ``'M'``, "calendar month end"
885+
:class:`~pandas.tseries.offsets.MonthEnd`, ``'ME'``, "calendar month end"
886886
:class:`~pandas.tseries.offsets.MonthBegin`, ``'MS'``, "calendar month begin"
887887
:class:`~pandas.tseries.offsets.BMonthEnd` or :class:`~pandas.tseries.offsets.BusinessMonthEnd`, ``'BM'``, "business month end"
888888
:class:`~pandas.tseries.offsets.BMonthBegin` or :class:`~pandas.tseries.offsets.BusinessMonthBegin`, ``'BMS'``, "business month begin"
@@ -1246,7 +1246,7 @@ frequencies. We will refer to these aliases as *offset aliases*.
12461246
"C", "custom business day frequency"
12471247
"D", "calendar day frequency"
12481248
"W", "weekly frequency"
1249-
"M", "month end frequency"
1249+
"ME", "month end frequency"
12501250
"SM", "semi-month end frequency (15th and end of month)"
12511251
"BM", "business month end frequency"
12521252
"CBM", "custom business month end frequency"
@@ -1690,7 +1690,7 @@ the end of the interval.
16901690
.. warning::
16911691

16921692
The default values for ``label`` and ``closed`` is '**left**' for all
1693-
frequency offsets except for 'M', 'A', 'Q', 'BM', 'BA', 'BQ', and 'W'
1693+
frequency offsets except for 'ME', 'A', 'Q', 'BM', 'BA', 'BQ', and 'W'
16941694
which all have a default of 'right'.
16951695

16961696
This might unintendedly lead to looking ahead, where the value for a later
@@ -1856,15 +1856,15 @@ to resample based on datetimelike column in the frame, it can passed to the
18561856
),
18571857
)
18581858
df
1859-
df.resample("M", on="date")[["a"]].sum()
1859+
df.resample("ME", on="date")[["a"]].sum()
18601860
18611861
Similarly, if you instead want to resample by a datetimelike
18621862
level of ``MultiIndex``, its name or location can be passed to the
18631863
``level`` keyword.
18641864

18651865
.. ipython:: python
18661866
1867-
df.resample("M", level="d")[["a"]].sum()
1867+
df.resample("ME", level="d")[["a"]].sum()
18681868
18691869
.. _timeseries.iterating-label:
18701870

@@ -2137,7 +2137,7 @@ The ``period`` dtype can be used in ``.astype(...)``. It allows one to change th
21372137
pi.astype("datetime64[ns]")
21382138
21392139
# convert to PeriodIndex
2140-
dti = pd.date_range("2011-01-01", freq="M", periods=3)
2140+
dti = pd.date_range("2011-01-01", freq="ME", periods=3)
21412141
dti
21422142
dti.astype("period[M]")
21432143
@@ -2256,7 +2256,7 @@ and vice-versa using ``to_timestamp``:
22562256

22572257
.. ipython:: python
22582258
2259-
rng = pd.date_range("1/1/2012", periods=5, freq="M")
2259+
rng = pd.date_range("1/1/2012", periods=5, freq="ME")
22602260
22612261
ts = pd.Series(np.random.randn(len(rng)), index=rng)
22622262

doc/source/whatsnew/v0.14.0.rst

+14-4
Original file line numberDiff line numberDiff line change
@@ -860,10 +860,20 @@ Enhancements
860860
datetime.datetime(2013, 9, 5, 10, 0)]})
861861
df
862862
863-
df.pivot_table(values='Quantity',
864-
index=pd.Grouper(freq='M', key='Date'),
865-
columns=pd.Grouper(freq='M', key='PayDay'),
866-
aggfunc="sum")
863+
.. code-block:: ipython
864+
865+
In [75]: df.pivot_table(values='Quantity',
866+
....: index=pd.Grouper(freq='M', key='Date'),
867+
....: columns=pd.Grouper(freq='M', key='PayDay'),
868+
....: aggfunc="sum")
869+
Out[75]:
870+
PayDay 2013-09-30 2013-10-31 2013-11-30
871+
Date
872+
2013-09-30 NaN 3.0 NaN
873+
2013-10-31 6.0 NaN 1.0
874+
2013-11-30 NaN 9.0 NaN
875+
876+
[3 rows x 3 columns]
867877
868878
- Arrays of strings can be wrapped to a specified width (``str.wrap``) (:issue:`6999`)
869879
- Add :meth:`~Series.nsmallest` and :meth:`Series.nlargest` methods to Series, See :ref:`the docs <basics.nsorted>` (:issue:`3960`)

doc/source/whatsnew/v0.18.0.rst

+17-2
Original file line numberDiff line numberDiff line change
@@ -837,9 +837,24 @@ Previously
837837
838838
New API
839839

840-
.. ipython:: python
840+
.. code-block:: ipython
841841
842-
s.resample('M').ffill()
842+
In [91]: s.resample('M').ffill()
843+
Out[91]:
844+
2010-03-31 0
845+
2010-04-30 0
846+
2010-05-31 0
847+
2010-06-30 1
848+
2010-07-31 1
849+
2010-08-31 1
850+
2010-09-30 2
851+
2010-10-31 2
852+
2010-11-30 2
853+
2010-12-31 3
854+
2011-01-31 3
855+
2011-02-28 3
856+
2011-03-31 4
857+
Freq: M, Length: 13, dtype: int64
843858
844859
.. note::
845860

doc/source/whatsnew/v0.19.0.rst

+20-2
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,26 @@ Other enhancements
498498
),
499499
)
500500
df
501-
df.resample("M", on="date")[["a"]].sum()
502-
df.resample("M", level="d")[["a"]].sum()
501+
502+
.. code-block:: ipython
503+
504+
In [74]: df.resample("M", on="date")[["a"]].sum()
505+
Out[74]:
506+
a
507+
date
508+
2015-01-31 6
509+
2015-02-28 4
510+
511+
[2 rows x 1 columns]
512+
513+
In [75]: df.resample("M", level="d")[["a"]].sum()
514+
Out[75]:
515+
a
516+
d
517+
2015-01-31 6
518+
2015-02-28 4
519+
520+
[2 rows x 1 columns]
503521
504522
- The ``.get_credentials()`` method of ``GbqConnector`` can now first try to fetch `the application default credentials <https://developers.google.com/identity/protocols/application-default-credentials>`__. See the docs for more details (:issue:`13577`).
505523
- The ``.tz_localize()`` method of ``DatetimeIndex`` and ``Timestamp`` has gained the ``errors`` keyword, so you can potentially coerce nonexistent timestamps to ``NaT``. The default behavior remains to raising a ``NonExistentTimeError`` (:issue:`13057`)

doc/source/whatsnew/v2.0.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Below is a possibly non-exhaustive list of changes:
7676

7777
.. ipython:: python
7878
79-
idx = pd.date_range(start='1/1/2018', periods=3, freq='M')
79+
idx = pd.date_range(start='1/1/2018', periods=3, freq='ME')
8080
idx.array.year
8181
idx.year
8282

doc/source/whatsnew/v2.2.0.rst

+25
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,31 @@ Other API changes
177177

178178
Deprecations
179179
~~~~~~~~~~~~
180+
181+
Deprecate alias ``M`` in favour of ``ME`` for offsets
182+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
183+
184+
The alias ``M`` is deprecated in favour of ``ME`` for offsets, please use ``ME`` for "month end" instead of ``M`` (:issue:`9586`)
185+
186+
For example:
187+
188+
*Previous behavior*:
189+
190+
.. code-block:: ipython
191+
192+
In [7]: pd.date_range('2020-01-01', periods=3, freq='M')
193+
Out [7]:
194+
DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'],
195+
dtype='datetime64[ns]', freq='M')
196+
197+
*Future behavior*:
198+
199+
.. ipython:: python
200+
201+
pd.date_range('2020-01-01', periods=3, freq='ME')
202+
203+
Other Deprecations
204+
^^^^^^^^^^^^^^^^^^
180205
- Changed :meth:`Timedelta.resolution_string` to return ``min``, ``s``, ``ms``, ``us``, and ``ns`` instead of ``T``, ``S``, ``L``, ``U``, and ``N``, for compatibility with respective deprecations in frequency aliases (:issue:`52536`)
181206
- Deprecated allowing non-keyword arguments in :meth:`DataFrame.to_clipboard`. (:issue:`54229`)
182207
- Deprecated allowing non-keyword arguments in :meth:`DataFrame.to_csv` except ``path_or_buf``. (:issue:`54229`)

pandas/_libs/tslibs/dtypes.pxd

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ cpdef int64_t periods_per_second(NPY_DATETIMEUNIT reso) except? -1
1111
cpdef NPY_DATETIMEUNIT get_supported_reso(NPY_DATETIMEUNIT reso)
1212
cpdef bint is_supported_unit(NPY_DATETIMEUNIT reso)
1313

14+
cpdef freq_to_period_freqstr(freq_n, freq_name)
15+
cdef dict c_OFFSET_TO_PERIOD_FREQSTR
1416
cdef dict c_DEPR_ABBREVS
1517
cdef dict attrname_to_abbrevs
1618
cdef dict npy_unit_to_attrname

pandas/_libs/tslibs/dtypes.pyi

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ from pandas._libs.tslibs.timedeltas import UnitChoices
66
# are imported in tests.
77
_attrname_to_abbrevs: dict[str, str]
88
_period_code_map: dict[str, int]
9+
OFFSET_TO_PERIOD_FREQSTR: dict[str, str]
910
DEPR_ABBREVS: dict[str, UnitChoices]
1011

1112
def periods_per_day(reso: int) -> int: ...
@@ -14,6 +15,7 @@ def is_supported_unit(reso: int) -> bool: ...
1415
def npy_unit_to_abbrev(reso: int) -> str: ...
1516
def get_supported_reso(reso: int) -> int: ...
1617
def abbrev_to_npy_unit(abbrev: str) -> int: ...
18+
def freq_to_period_freqstr(freq_n: int, freq_name: str) -> str: ...
1719

1820
class PeriodDtypeBase:
1921
_dtype_code: int # PeriodDtypeCode

pandas/_libs/tslibs/dtypes.pyx

+39-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ from pandas._libs.tslibs.np_datetime cimport (
1313

1414
import_pandas_datetime()
1515

16-
1716
cdef class PeriodDtypeBase:
1817
"""
1918
Similar to an actual dtype, this contains all of the information
@@ -186,6 +185,45 @@ _attrname_to_abbrevs = {
186185
cdef dict attrname_to_abbrevs = _attrname_to_abbrevs
187186
cdef dict _abbrev_to_attrnames = {v: k for k, v in attrname_to_abbrevs.items()}
188187

188+
OFFSET_TO_PERIOD_FREQSTR: dict = {
189+
"WEEKDAY": "D",
190+
"EOM": "M",
191+
"BM": "M",
192+
"BQS": "Q",
193+
"QS": "Q",
194+
"BQ": "Q",
195+
"BA": "A",
196+
"AS": "A",
197+
"BAS": "A",
198+
"MS": "M",
199+
"D": "D",
200+
"B": "B",
201+
"min": "min",
202+
"s": "s",
203+
"ms": "ms",
204+
"us": "us",
205+
"ns": "ns",
206+
"H": "H",
207+
"Q": "Q",
208+
"A": "A",
209+
"W": "W",
210+
"ME": "M",
211+
"Y": "A",
212+
"BY": "A",
213+
"YS": "A",
214+
"BYS": "A",
215+
}
216+
cdef dict c_OFFSET_TO_PERIOD_FREQSTR = OFFSET_TO_PERIOD_FREQSTR
217+
218+
cpdef freq_to_period_freqstr(freq_n, freq_name):
219+
if freq_n == 1:
220+
freqstr = f"""{c_OFFSET_TO_PERIOD_FREQSTR.get(
221+
freq_name, freq_name)}"""
222+
else:
223+
freqstr = f"""{freq_n}{c_OFFSET_TO_PERIOD_FREQSTR.get(
224+
freq_name, freq_name)}"""
225+
return freqstr
226+
189227
# Map deprecated resolution abbreviations to correct resolution abbreviations
190228
DEPR_ABBREVS: dict[str, str]= {
191229
"T": "min",

pandas/_libs/tslibs/offsets.pxd

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from numpy cimport int64_t
22

33

4-
cpdef to_offset(object obj)
4+
cpdef to_offset(object obj, bint is_period=*)
55
cdef bint is_offset_object(object obj)
66
cdef bint is_tick_object(object obj)
77

pandas/_libs/tslibs/offsets.pyi

+3-3
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ class SingleConstructorOffset(BaseOffset):
103103
def __reduce__(self): ...
104104

105105
@overload
106-
def to_offset(freq: None) -> None: ...
106+
def to_offset(freq: None, is_period: bool = ...) -> None: ...
107107
@overload
108-
def to_offset(freq: _BaseOffsetT) -> _BaseOffsetT: ...
108+
def to_offset(freq: _BaseOffsetT, is_period: bool = ...) -> _BaseOffsetT: ...
109109
@overload
110-
def to_offset(freq: timedelta | str) -> BaseOffset: ...
110+
def to_offset(freq: timedelta | str, is_period: bool = ...) -> BaseOffset: ...
111111

112112
class Tick(SingleConstructorOffset):
113113
_creso: int

0 commit comments

Comments
 (0)