Skip to content

Commit 039edee

Browse files
natmokvalmroeschke
andauthored
DEPR: lowercase strings w, d, b and c denoting frequencies in Week, Day, BusinessDay and CustomBusinessDay classes (#58998)
* deprecate lowercase 'd' * fix tests, add tests * deprecate lowercase alias 'b', fix tests * fix tests and docs * fix tests, fix an example in v0.20.0 * deprecate 'c',fix examples in v0.22.0, add tests and a note to v3.0.0 * correct examples in whatsnew * update examples in user_guide/io.rst --------- Co-authored-by: Matthew Roeschke <[email protected]>
1 parent e3af7c6 commit 039edee

33 files changed

+351
-139
lines changed

doc/source/user_guide/io.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -2161,7 +2161,7 @@ a JSON string with two fields, ``schema`` and ``data``.
21612161
{
21622162
"A": [1, 2, 3],
21632163
"B": ["a", "b", "c"],
2164-
"C": pd.date_range("2016-01-01", freq="d", periods=3),
2164+
"C": pd.date_range("2016-01-01", freq="D", periods=3),
21652165
},
21662166
index=pd.Index(range(3), name="idx"),
21672167
)
@@ -2270,7 +2270,7 @@ round-trippable manner.
22702270
{
22712271
"foo": [1, 2, 3, 4],
22722272
"bar": ["a", "b", "c", "d"],
2273-
"baz": pd.date_range("2018-01-01", freq="d", periods=4),
2273+
"baz": pd.date_range("2018-01-01", freq="D", periods=4),
22742274
"qux": pd.Categorical(["a", "b", "c", "c"]),
22752275
},
22762276
index=pd.Index(range(4), name="idx"),

doc/source/whatsnew/v0.18.0.rst

+19-6
Original file line numberDiff line numberDiff line change
@@ -322,15 +322,28 @@ Tz-aware are rounded, floored and ceiled in local times
322322
323323
Timedeltas
324324

325-
.. ipython:: python
325+
.. code-block:: ipython
326+
327+
In [37]: t = pd.timedelta_range('1 days 2 hr 13 min 45 us', periods=3, freq='d')
326328
327-
t = pd.timedelta_range('1 days 2 hr 13 min 45 us', periods=3, freq='d')
328-
t
329-
t.round('10min')
329+
In [38]: t
330+
Out[38]:
331+
TimedeltaIndex(['1 days 02:13:00.000045', '2 days 02:13:00.000045',
332+
'3 days 02:13:00.000045'],
333+
dtype='timedelta64[ns]', freq='D')
334+
335+
In [39]: t.round('10min')
336+
Out[39]:
337+
TimedeltaIndex(['1 days 02:10:00', '2 days 02:10:00',
338+
'3 days 02:10:00'],
339+
dtype='timedelta64[ns]', freq=None)
330340
331341
# Timedelta scalar
332-
t[0]
333-
t[0].round('2h')
342+
In [40]: t[0]
343+
Out[40]: Timedelta('1 days 02:13:00.000045')
344+
345+
In [41]: t[0].round('2h')
346+
Out[41]: Timedelta('1 days 02:00:00')
334347
335348
336349
In addition, ``.round()``, ``.floor()`` and ``.ceil()`` will be available through the ``.dt`` accessor of ``Series``.

doc/source/whatsnew/v0.20.0.rst

+19-8
Original file line numberDiff line numberDiff line change
@@ -308,15 +308,26 @@ The new orient ``'table'`` for :meth:`DataFrame.to_json`
308308
will generate a `Table Schema`_ compatible string representation of
309309
the data.
310310

311-
.. ipython:: python
311+
.. code-block:: ipython
312312
313-
df = pd.DataFrame(
314-
{'A': [1, 2, 3],
315-
'B': ['a', 'b', 'c'],
316-
'C': pd.date_range('2016-01-01', freq='d', periods=3)},
317-
index=pd.Index(range(3), name='idx'))
318-
df
319-
df.to_json(orient='table')
313+
In [38]: df = pd.DataFrame(
314+
....: {'A': [1, 2, 3],
315+
....: 'B': ['a', 'b', 'c'],
316+
....: 'C': pd.date_range('2016-01-01', freq='d', periods=3)},
317+
....: index=pd.Index(range(3), name='idx'))
318+
In [39]: df
319+
Out[39]:
320+
A B C
321+
idx
322+
0 1 a 2016-01-01
323+
1 2 b 2016-01-02
324+
2 3 c 2016-01-03
325+
326+
[3 rows x 3 columns]
327+
328+
In [40]: df.to_json(orient='table')
329+
Out[40]:
330+
'{"schema":{"fields":[{"name":"idx","type":"integer"},{"name":"A","type":"integer"},{"name":"B","type":"string"},{"name":"C","type":"datetime"}],"primaryKey":["idx"],"pandas_version":"1.4.0"},"data":[{"idx":0,"A":1,"B":"a","C":"2016-01-01T00:00:00.000"},{"idx":1,"A":2,"B":"b","C":"2016-01-02T00:00:00.000"},{"idx":2,"A":3,"B":"c","C":"2016-01-03T00:00:00.000"}]}'
320331
321332
322333
See :ref:`IO: Table Schema for more information <io.table_schema>`.

doc/source/whatsnew/v0.22.0.rst

+16-5
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,27 @@ sum and ``1`` for product.
157157
158158
*pandas 0.22.0*
159159

160-
.. ipython:: python
160+
.. code-block:: ipython
161+
162+
In [11]: s = pd.Series([1, 1, np.nan, np.nan],
163+
....: index=pd.date_range("2017", periods=4))
161164
162-
s = pd.Series([1, 1, np.nan, np.nan], index=pd.date_range("2017", periods=4))
163-
s.resample("2d").sum()
165+
In [12]: s.resample("2d").sum()
166+
Out[12]:
167+
2017-01-01 2.0
168+
2017-01-03 0.0
169+
Freq: 2D, Length: 2, dtype: float64
164170
165171
To restore the 0.21 behavior of returning ``NaN``, use ``min_count>=1``.
166172

167-
.. ipython:: python
173+
.. code-block:: ipython
174+
175+
In [13]: s.resample("2d").sum(min_count=1)
176+
Out[13]:
177+
2017-01-01 2.0
178+
2017-01-03 NaN
179+
Freq: 2D, Length: 2, dtype: float64
168180
169-
s.resample("2d").sum(min_count=1)
170181
171182
In particular, upsampling and taking the sum or product is affected, as
172183
upsampling introduces missing values even if the original series was

doc/source/whatsnew/v0.23.0.rst

+48-12
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,55 @@ JSON read/write round-trippable with ``orient='table'``
5050

5151
A ``DataFrame`` can now be written to and subsequently read back via JSON while preserving metadata through usage of the ``orient='table'`` argument (see :issue:`18912` and :issue:`9146`). Previously, none of the available ``orient`` values guaranteed the preservation of dtypes and index names, amongst other metadata.
5252

53-
.. ipython:: python
53+
.. code-block:: ipython
5454
55-
df = pd.DataFrame({'foo': [1, 2, 3, 4],
56-
'bar': ['a', 'b', 'c', 'd'],
57-
'baz': pd.date_range('2018-01-01', freq='d', periods=4),
58-
'qux': pd.Categorical(['a', 'b', 'c', 'c'])},
59-
index=pd.Index(range(4), name='idx'))
60-
df
61-
df.dtypes
62-
df.to_json('test.json', orient='table')
63-
new_df = pd.read_json('test.json', orient='table')
64-
new_df
65-
new_df.dtypes
55+
In [1]: df = pd.DataFrame({'foo': [1, 2, 3, 4],
56+
...: 'bar': ['a', 'b', 'c', 'd'],
57+
...: 'baz': pd.date_range('2018-01-01', freq='d', periods=4),
58+
...: 'qux': pd.Categorical(['a', 'b', 'c', 'c'])},
59+
...: index=pd.Index(range(4), name='idx'))
60+
61+
In [2]: df
62+
Out[2]:
63+
foo bar baz qux
64+
idx
65+
0 1 a 2018-01-01 a
66+
1 2 b 2018-01-02 b
67+
2 3 c 2018-01-03 c
68+
3 4 d 2018-01-04 c
69+
70+
[4 rows x 4 columns]
71+
72+
In [3]: df.dtypes
73+
Out[3]:
74+
foo int64
75+
bar object
76+
baz datetime64[ns]
77+
qux category
78+
Length: 4, dtype: object
79+
80+
In [4]: df.to_json('test.json', orient='table')
81+
82+
In [5]: new_df = pd.read_json('test.json', orient='table')
83+
84+
In [6]: new_df
85+
Out[6]:
86+
foo bar baz qux
87+
idx
88+
0 1 a 2018-01-01 a
89+
1 2 b 2018-01-02 b
90+
2 3 c 2018-01-03 c
91+
3 4 d 2018-01-04 c
92+
93+
[4 rows x 4 columns]
94+
95+
In [7]: new_df.dtypes
96+
Out[7]:
97+
foo int64
98+
bar object
99+
baz datetime64[ns]
100+
qux category
101+
Length: 4, dtype: object
66102
67103
Please note that the string ``index`` is not supported with the round trip format, as it is used by default in ``write_json`` to indicate a missing index name.
68104

doc/source/whatsnew/v3.0.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ Other Deprecations
279279
- Deprecated allowing non-keyword arguments in :meth:`Series.to_markdown` except ``buf``. (:issue:`57280`)
280280
- Deprecated allowing non-keyword arguments in :meth:`Series.to_string` except ``buf``. (:issue:`57280`)
281281
- Deprecated behavior of :meth:`Series.dt.to_pytimedelta`, in a future version this will return a :class:`Series` containing python ``datetime.timedelta`` objects instead of an ``ndarray`` of timedelta; this matches the behavior of other :meth:`Series.dt` properties. (:issue:`57463`)
282+
- Deprecated lowercase strings ``d``, ``b`` and ``c`` denoting frequencies in :class:`Day`, :class:`BusinessDay` and :class:`CustomBusinessDay` in favour of ``D``, ``B`` and ``C`` (:issue:`58998`)
283+
- Deprecated lowercase strings ``w``, ``w-mon``, ``w-tue``, etc. denoting frequencies in :class:`Week` in favour of ``W``, ``W-MON``, ``W-TUE``, etc. (:issue:`58998`)
282284
- Deprecated parameter ``method`` in :meth:`DataFrame.reindex_like` / :meth:`Series.reindex_like` (:issue:`58667`)
283285
- Deprecated strings ``w``, ``d``, ``MIN``, ``MS``, ``US`` and ``NS`` denoting units in :class:`Timedelta` in favour of ``W``, ``D``, ``min``, ``ms``, ``us`` and ``ns`` (:issue:`59051`)
284286
- Deprecated using ``epoch`` date format in :meth:`DataFrame.to_json` and :meth:`Series.to_json`, use ``iso`` instead. (:issue:`57063`)

pandas/_libs/tslibs/dtypes.pyx

+10
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,16 @@ cdef dict c_DEPR_UNITS = {
359359

360360
cdef dict c_PERIOD_AND_OFFSET_DEPR_FREQSTR = {
361361
"w": "W",
362+
"w-mon": "W-MON",
363+
"w-tue": "W-TUE",
364+
"w-wed": "W-WED",
365+
"w-thu": "W-THU",
366+
"w-fri": "W-FRI",
367+
"w-sat": "W-SAT",
368+
"w-sun": "W-SUN",
369+
"d": "D",
370+
"b": "B",
371+
"c": "C",
362372
"MIN": "min",
363373
}
364374

pandas/_libs/tslibs/offsets.pyx

+10-10
Original file line numberDiff line numberDiff line change
@@ -4890,16 +4890,16 @@ cpdef to_offset(freq, bint is_period=False):
48904890
)
48914891
name = c_PERIOD_TO_OFFSET_FREQSTR.get(name.upper())
48924892

4893-
if name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR:
4894-
warnings.warn(
4895-
f"\'{name}\' is deprecated and will be removed "
4896-
f"in a future version, please use "
4897-
f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\' "
4898-
f" instead.",
4899-
FutureWarning,
4900-
stacklevel=find_stack_level(),
4901-
)
4902-
name = c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)
4893+
if name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR:
4894+
warnings.warn(
4895+
f"\'{name}\' is deprecated and will be removed "
4896+
f"in a future version, please use "
4897+
f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\' "
4898+
f" instead.",
4899+
FutureWarning,
4900+
stacklevel=find_stack_level(),
4901+
)
4902+
name = c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)
49034903
if sep != "" and not sep.isspace():
49044904
raise ValueError("separator must be spaces")
49054905
prefix = _lite_rule_alias.get(name) or name

pandas/io/json/_table_schema.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def build_table_schema(
275275
>>> df = pd.DataFrame(
276276
... {'A': [1, 2, 3],
277277
... 'B': ['a', 'b', 'c'],
278-
... 'C': pd.date_range('2016-01-01', freq='d', periods=3),
278+
... 'C': pd.date_range('2016-01-01', freq='D', periods=3),
279279
... }, index=pd.Index(range(3), name='idx'))
280280
>>> build_table_schema(df)
281281
{'fields': \

pandas/tests/arithmetic/test_period.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,7 @@ def test_parr_add_timedeltalike_minute_gt1(self, three_days, box_with_array):
10861086
with pytest.raises(TypeError, match=msg):
10871087
other - rng
10881088

1089-
@pytest.mark.parametrize("freqstr", ["5ns", "5us", "5ms", "5s", "5min", "5h", "5d"])
1089+
@pytest.mark.parametrize("freqstr", ["5ns", "5us", "5ms", "5s", "5min", "5h", "5D"])
10901090
def test_parr_add_timedeltalike_tick_gt1(self, three_days, freqstr, box_with_array):
10911091
# GH#23031 adding a time-delta-like offset to a PeriodArray that has
10921092
# tick-like frequency with n != 1

pandas/tests/frame/methods/test_astype.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -715,8 +715,12 @@ def test_astype_ignores_errors_for_extension_dtypes(self, data, dtype, errors):
715715
df.astype(float, errors=errors)
716716

717717
def test_astype_tz_conversion(self):
718-
# GH 35973
719-
val = {"tz": date_range("2020-08-30", freq="d", periods=2, tz="Europe/London")}
718+
# GH 35973, GH#58998
719+
msg = "'d' is deprecated and will be removed in a future version."
720+
with tm.assert_produces_warning(FutureWarning, match=msg):
721+
val = {
722+
"tz": date_range("2020-08-30", freq="d", periods=2, tz="Europe/London")
723+
}
720724
df = DataFrame(val)
721725
result = df.astype({"tz": "datetime64[ns, Europe/Berlin]"})
722726

@@ -727,7 +731,7 @@ def test_astype_tz_conversion(self):
727731
@pytest.mark.parametrize("tz", ["UTC", "Europe/Berlin"])
728732
def test_astype_tz_object_conversion(self, tz):
729733
# GH 35973
730-
val = {"tz": date_range("2020-08-30", freq="d", periods=2, tz="Europe/London")}
734+
val = {"tz": date_range("2020-08-30", freq="D", periods=2, tz="Europe/London")}
731735
expected = DataFrame(val)
732736

733737
# convert expected to object dtype from other tz str (independently tested)

pandas/tests/frame/methods/test_reindex.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,10 @@ def test_reindex_axes(self):
754754
index=[datetime(2012, 1, 1), datetime(2012, 1, 2), datetime(2012, 1, 3)],
755755
columns=["a", "b", "c"],
756756
)
757-
time_freq = date_range("2012-01-01", "2012-01-03", freq="d")
757+
758+
msg = "'d' is deprecated and will be removed in a future version."
759+
with tm.assert_produces_warning(FutureWarning, match=msg):
760+
time_freq = date_range("2012-01-01", "2012-01-03", freq="d")
758761
some_cols = ["a", "b"]
759762

760763
index_freq = df.reindex(index=time_freq).index.freq

pandas/tests/frame/test_query_eval.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ def test_check_tz_aware_index_query(self, tz_aware_fixture):
763763
# https://github.com/pandas-dev/pandas/issues/29463
764764
tz = tz_aware_fixture
765765
df_index = date_range(
766-
start="2019-01-01", freq="1d", periods=10, tz=tz, name="time"
766+
start="2019-01-01", freq="1D", periods=10, tz=tz, name="time"
767767
)
768768
expected = DataFrame(index=df_index)
769769
df = DataFrame(index=df_index)

pandas/tests/groupby/test_groupby_dropna.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ def test_groupby_drop_nan_with_multi_index():
420420
),
421421
),
422422
"datetime64[ns]",
423-
"period[d]",
423+
"period[D]",
424424
"Sparse[float]",
425425
],
426426
)
@@ -437,7 +437,7 @@ def test_no_sort_keep_na(sequence_index, dtype, test_series, as_index):
437437
# Unique values to use for grouper, depends on dtype
438438
if dtype in ("string", "string[pyarrow]"):
439439
uniques = {"x": "x", "y": "y", "z": pd.NA}
440-
elif dtype in ("datetime64[ns]", "period[d]"):
440+
elif dtype in ("datetime64[ns]", "period[D]"):
441441
uniques = {"x": "2016-01-01", "y": "2017-01-01", "z": pd.NA}
442442
else:
443443
uniques = {"x": 1, "y": 2, "z": np.nan}

pandas/tests/indexes/datetimes/methods/test_snap.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import pandas._testing as tm
88

99

10+
@pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning")
11+
@pytest.mark.filterwarnings("ignore:Period with BDay freq:FutureWarning")
1012
@pytest.mark.parametrize("tz", [None, "Asia/Shanghai", "Europe/Berlin"])
1113
@pytest.mark.parametrize("name", [None, "my_dti"])
1214
def test_dti_snap(name, tz, unit):
@@ -27,7 +29,9 @@ def test_dti_snap(name, tz, unit):
2729
dti = dti.as_unit(unit)
2830

2931
result = dti.snap(freq="W-MON")
30-
expected = date_range("12/31/2001", "1/7/2002", name=name, tz=tz, freq="w-mon")
32+
msg = "'w-mon' is deprecated and will be removed in a future version."
33+
with tm.assert_produces_warning(FutureWarning, match=msg):
34+
expected = date_range("12/31/2001", "1/7/2002", name=name, tz=tz, freq="w-mon")
3135
expected = expected.repeat([3, 4])
3236
expected = expected.as_unit(unit)
3337
tm.assert_index_equal(result, expected)
@@ -37,7 +41,9 @@ def test_dti_snap(name, tz, unit):
3741

3842
result = dti.snap(freq="B")
3943

40-
expected = date_range("1/1/2002", "1/7/2002", name=name, tz=tz, freq="b")
44+
msg = "'b' is deprecated and will be removed in a future version."
45+
with tm.assert_produces_warning(FutureWarning, match=msg):
46+
expected = date_range("1/1/2002", "1/7/2002", name=name, tz=tz, freq="b")
4147
expected = expected.repeat([1, 1, 1, 2, 2])
4248
expected = expected.as_unit(unit)
4349
tm.assert_index_equal(result, expected)

pandas/tests/indexes/datetimes/test_date_range.py

+20
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,26 @@ def test_frequency_A_raises(self, freq):
791791
with pytest.raises(ValueError, match=msg):
792792
date_range("1/1/2000", periods=2, freq=freq)
793793

794+
@pytest.mark.parametrize(
795+
"freq,freq_depr",
796+
[
797+
("2W", "2w"),
798+
("2W-WED", "2w-wed"),
799+
("2B", "2b"),
800+
("2D", "2d"),
801+
("2C", "2c"),
802+
],
803+
)
804+
def test_date_range_depr_lowercase_frequency(self, freq, freq_depr):
805+
# GH#58998
806+
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed "
807+
"in a future version."
808+
809+
expected = date_range("1/1/2000", periods=4, freq=freq)
810+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
811+
result = date_range("1/1/2000", periods=4, freq=freq_depr)
812+
tm.assert_index_equal(result, expected)
813+
794814

795815
class TestDateRangeTZ:
796816
"""Tests for date_range with timezones"""

pandas/tests/indexes/datetimes/test_partial_slicing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def test_string_index_series_name_converted(self):
3535
def test_stringified_slice_with_tz(self):
3636
# GH#2658
3737
start = "2013-01-07"
38-
idx = date_range(start=start, freq="1d", periods=10, tz="US/Eastern")
38+
idx = date_range(start=start, freq="1D", periods=10, tz="US/Eastern")
3939
df = DataFrame(np.arange(10), index=idx)
4040
df["2013-01-14 23:44:34.437768-05:00":] # no exception here
4141

0 commit comments

Comments
 (0)