Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEPR: deprecate units 'w', 'd', 'MS', 'US', 'NS' for Timedelta in favor of 'W', 'D', 'ms', 'us', 'ns' #59051

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/source/user_guide/timedeltas.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ You can construct a ``Timedelta`` scalar through various arguments, including `I
pd.Timedelta(days=1, seconds=1)

# integers with a unit
pd.Timedelta(1, unit="d")
pd.Timedelta(1, unit="D")

# from a datetime.timedelta/np.timedelta64
pd.Timedelta(datetime.timedelta(days=1, seconds=1))
Expand Down Expand Up @@ -94,7 +94,7 @@ is numeric:
.. ipython:: python

pd.to_timedelta(np.arange(5), unit="s")
pd.to_timedelta(np.arange(5), unit="d")
pd.to_timedelta(np.arange(5), unit="D")

.. warning::
If a string or array of strings is passed as an input then the ``unit`` keyword
Expand Down
24 changes: 18 additions & 6 deletions doc/source/whatsnew/v0.13.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -523,13 +523,25 @@ Enhancements
Using the new top-level ``to_timedelta``, you can convert a scalar or array from the standard
timedelta format (produced by ``to_csv``) into a timedelta type (``np.timedelta64`` in ``nanoseconds``).

.. ipython:: python
.. code-block:: ipython

In [53]: pd.to_timedelta('1 days 06:05:01.00003')
Out[53]: Timedelta('1 days 06:05:01.000030')

In [54]: pd.to_timedelta('15.5us')
Out[54]: Timedelta('0 days 00:00:00.000015500')

In [55]: pd.to_timedelta(['1 days 06:05:01.00003', '15.5us', 'nan'])
Out[55]: TimedeltaIndex(['1 days 06:05:01.000030', '0 days 00:00:00.000015500', NaT], dtype='timedelta64[ns]', freq=None)

In [56]: pd.to_timedelta(np.arange(5), unit='s')
Out[56]:
TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:01', '0 days 00:00:02',
'0 days 00:00:03', '0 days 00:00:04'],
dtype='timedelta64[ns]', freq=None)

pd.to_timedelta('1 days 06:05:01.00003')
pd.to_timedelta('15.5us')
pd.to_timedelta(['1 days 06:05:01.00003', '15.5us', 'nan'])
pd.to_timedelta(np.arange(5), unit='s')
pd.to_timedelta(np.arange(5), unit='d')
In [57]: pd.to_timedelta(np.arange(5), unit='d')
Out[57]: TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], dtype='timedelta64[ns]', freq=None)

A Series of dtype ``timedelta64[ns]`` can now be divided by another
``timedelta64[ns]`` object, or astyped to yield a ``float64`` dtyped Series. This
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ Other Deprecations
- Deprecated allowing non-keyword arguments in :meth:`Series.to_string` except ``buf``. (:issue:`57280`)
- 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`)
- Deprecated parameter ``method`` in :meth:`DataFrame.reindex_like` / :meth:`Series.reindex_like` (:issue:`58667`)
- 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`)
- Deprecated using ``epoch`` date format in :meth:`DataFrame.to_json` and :meth:`Series.to_json`, use ``iso`` instead. (:issue:`57063`)

.. ---------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions pandas/_libs/tslibs/dtypes.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ cdef dict c_OFFSET_TO_PERIOD_FREQSTR
cdef dict c_PERIOD_TO_OFFSET_FREQSTR
cdef dict c_OFFSET_RENAMED_FREQSTR
cdef dict c_DEPR_ABBREVS
cdef dict c_DEPR_UNITS
cdef dict c_PERIOD_AND_OFFSET_DEPR_FREQSTR
cdef dict attrname_to_abbrevs
cdef dict npy_unit_to_attrname
Expand Down
11 changes: 11 additions & 0 deletions pandas/_libs/tslibs/dtypes.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@ cdef dict c_DEPR_ABBREVS = {
"S": "s",
}

cdef dict c_DEPR_UNITS = {
"w": "W",
"d": "D",
"H": "h",
"MIN": "min",
"S": "s",
"MS": "ms",
"US": "us",
"NS": "ns",
}

cdef dict c_PERIOD_AND_OFFSET_DEPR_FREQSTR = {
"w": "W",
"MIN": "min",
Expand Down
13 changes: 9 additions & 4 deletions pandas/_libs/tslibs/timedeltas.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ from pandas._libs.tslibs.conversion cimport (
precision_from_unit,
)
from pandas._libs.tslibs.dtypes cimport (
c_DEPR_ABBREVS,
c_DEPR_UNITS,
get_supported_reso,
is_supported_unit,
npy_unit_to_abbrev,
Expand Down Expand Up @@ -719,15 +719,15 @@ cpdef inline str parse_timedelta_unit(str unit):
return "ns"
elif unit == "M":
return unit
elif unit in c_DEPR_ABBREVS:
elif unit in c_DEPR_UNITS:
warnings.warn(
f"\'{unit}\' is deprecated and will be removed in a "
f"future version. Please use \'{c_DEPR_ABBREVS.get(unit)}\' "
f"future version. Please use \'{c_DEPR_UNITS.get(unit)}\' "
f"instead of \'{unit}\'.",
FutureWarning,
stacklevel=find_stack_level(),
)
unit = c_DEPR_ABBREVS[unit]
unit = c_DEPR_UNITS[unit]
try:
return timedelta_abbrevs[unit.lower()]
except KeyError:
Expand Down Expand Up @@ -1811,6 +1811,11 @@ class Timedelta(_Timedelta):
Values `H`, `T`, `S`, `L`, `U`, and `N` are deprecated in favour
of the values `h`, `min`, `s`, `ms`, `us`, and `ns`.

.. deprecated:: 3.0.0

Allowing the values `w`, `d`, `MIN`, `MS`, `US` and `NS` to denote units
are deprecated in favour of the values `W`, `D`, `min`, `ms`, `us` and `ns`.

**kwargs
Available kwargs: {days, seconds, microseconds,
milliseconds, minutes, hours, weeks}.
Expand Down
6 changes: 3 additions & 3 deletions pandas/core/arrays/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ def total_seconds(self) -> npt.NDArray[np.float64]:
--------
**Series**

>>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="d"))
>>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="D"))
>>> s
0 0 days
1 1 days
Expand All @@ -765,7 +765,7 @@ def total_seconds(self) -> npt.NDArray[np.float64]:

**TimedeltaIndex**

>>> idx = pd.to_timedelta(np.arange(5), unit="d")
>>> idx = pd.to_timedelta(np.arange(5), unit="D")
>>> idx
TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'],
dtype='timedelta64[ns]', freq=None)
Expand Down Expand Up @@ -809,7 +809,7 @@ def to_pytimedelta(self) -> npt.NDArray[np.object_]:
--------
For Series:

>>> ser = pd.Series(pd.to_timedelta([1, 2, 3], unit='d'))
>>> ser = pd.Series(pd.to_timedelta([1, 2, 3], unit='D'))
>>> ser
0 1 days
1 2 days
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexes/accessors.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ def to_pytimedelta(self) -> np.ndarray:

Examples
--------
>>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="d"))
>>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="D"))
>>> s
0 0 days
1 1 days
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/tools/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def to_timedelta(
TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:01', '0 days 00:00:02',
'0 days 00:00:03', '0 days 00:00:04'],
dtype='timedelta64[ns]', freq=None)
>>> pd.to_timedelta(np.arange(5), unit="d")
>>> pd.to_timedelta(np.arange(5), unit="D")
TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'],
dtype='timedelta64[ns]', freq=None)
"""
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/frame/indexing/test_mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,15 @@ def test_mask_stringdtype(frame_or_series):

def test_mask_where_dtype_timedelta():
# https://github.com/pandas-dev/pandas/issues/39548
df = DataFrame([Timedelta(i, unit="d") for i in range(5)])
df = DataFrame([Timedelta(i, unit="D") for i in range(5)])

expected = DataFrame(np.full(5, np.nan, dtype="timedelta64[ns]"))
tm.assert_frame_equal(df.mask(df.notna()), expected)

expected = DataFrame(
[np.nan, np.nan, np.nan, Timedelta("3 day"), Timedelta("4 day")]
)
tm.assert_frame_equal(df.where(df > Timedelta(2, unit="d")), expected)
tm.assert_frame_equal(df.where(df > Timedelta(2, unit="D")), expected)


def test_mask_return_dtype():
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/frame/methods/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def test_astype_str(self):
# see GH#9757
a = Series(date_range("2010-01-04", periods=5))
b = Series(date_range("3/6/2012 00:00", periods=5, tz="US/Eastern"))
c = Series([Timedelta(x, unit="d") for x in range(5)])
c = Series([Timedelta(x, unit="D") for x in range(5)])
d = Series(range(5))
e = Series([0.0, 0.2, 0.4, 0.6, 0.8])

Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/frame/methods/test_reset_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,8 @@ def test_reset_index_with_drop(
{"a": [pd.NaT, Timestamp("2020-01-01")], "b": [1, 2], "x": [11, 12]},
),
(
[(pd.NaT, 1), (pd.Timedelta(123, "d"), 2)],
{"a": [pd.NaT, pd.Timedelta(123, "d")], "b": [1, 2], "x": [11, 12]},
[(pd.NaT, 1), (pd.Timedelta(123, "D"), 2)],
{"a": [pd.NaT, pd.Timedelta(123, "D")], "b": [1, 2], "x": [11, 12]},
),
],
)
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/groupby/test_groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ def test_len_nan_group():

def test_groupby_timedelta_median():
# issue 57926
expected = Series(data=Timedelta("1d"), index=["foo"])
df = DataFrame({"label": ["foo", "foo"], "timedelta": [pd.NaT, Timedelta("1d")]})
expected = Series(data=Timedelta("1D"), index=["foo"])
df = DataFrame({"label": ["foo", "foo"], "timedelta": [pd.NaT, Timedelta("1D")]})
gb = df.groupby("label")["timedelta"]
actual = gb.median()
tm.assert_series_equal(actual, expected, check_names=False)
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/groupby/test_reductions.py
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ def test_groupby_sum_timedelta_with_nat():
df = DataFrame(
{
"a": [1, 1, 2, 2],
"b": [pd.Timedelta("1d"), pd.Timedelta("2d"), pd.Timedelta("3d"), pd.NaT],
"b": [pd.Timedelta("1D"), pd.Timedelta("2D"), pd.Timedelta("3D"), pd.NaT],
}
)
td3 = pd.Timedelta(days=3)
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/indexes/timedeltas/methods/test_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def test_tdi_shift_minutes(self):

def test_tdi_shift_int(self):
# GH#8083
tdi = pd.to_timedelta(range(5), unit="d")
tdi = pd.to_timedelta(range(5), unit="D")
trange = tdi._with_freq("infer") + pd.offsets.Hour(1)
result = trange.shift(1)
expected = TimedeltaIndex(
Expand All @@ -54,7 +54,7 @@ def test_tdi_shift_int(self):

def test_tdi_shift_nonstandard_freq(self):
# GH#8083
tdi = pd.to_timedelta(range(5), unit="d")
tdi = pd.to_timedelta(range(5), unit="D")
trange = tdi._with_freq("infer") + pd.offsets.Hour(1)
result = trange.shift(3, freq="2D 1s")
expected = TimedeltaIndex(
Expand Down
27 changes: 26 additions & 1 deletion pandas/tests/indexes/timedeltas/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def test_constructor_coverage(self):
# NumPy string array
strings = np.array(["1 days", "2 days", "3 days"])
result = TimedeltaIndex(strings)
expected = to_timedelta([1, 2, 3], unit="d")
expected = to_timedelta([1, 2, 3], unit="D")
tm.assert_index_equal(result, expected)

from_ints = TimedeltaIndex(expected.asi8)
Expand Down Expand Up @@ -239,3 +239,28 @@ def test_from_categorical(self):
ci = pd.CategoricalIndex(tdi)
result = TimedeltaIndex(ci)
tm.assert_index_equal(result, tdi)

@pytest.mark.parametrize(
"unit,unit_depr",
[
("W", "w"),
("D", "d"),
("min", "MIN"),
("s", "S"),
("h", "H"),
("ms", "MS"),
("us", "US"),
],
)
def test_unit_deprecated(self, unit, unit_depr):
# GH#52536, GH#59051
msg = f"'{unit_depr}' is deprecated and will be removed in a future version."

expected = TimedeltaIndex([f"1{unit}", f"2{unit}"])
with tm.assert_produces_warning(FutureWarning, match=msg):
result = TimedeltaIndex([f"1{unit_depr}", f"2{unit_depr}"])
tm.assert_index_equal(result, expected)

with tm.assert_produces_warning(FutureWarning, match=msg):
tdi = to_timedelta([1, 2], unit=unit_depr)
tm.assert_index_equal(tdi, expected)
2 changes: 1 addition & 1 deletion pandas/tests/indexes/timedeltas/test_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_delete_slice(self):

# reset freq to None
expected_3_5 = TimedeltaIndex(
["1 d", "2 d", "3 d", "7 d", "8 d", "9 d", "10d"], freq=None, name="idx"
["1 D", "2 D", "3 D", "7 D", "8 D", "9 D", "10D"], freq=None, name="idx"
)

cases = {
Expand Down
14 changes: 9 additions & 5 deletions pandas/tests/indexes/timedeltas/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@

class TestGetItem:
def test_getitem_slice_keeps_name(self):
# GH#4226
tdi = timedelta_range("1d", "5d", freq="h", name="timebucket")
# GH#4226, GH#59051
msg = "'d' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg):
tdi = timedelta_range("1d", "5d", freq="h", name="timebucket")
assert tdi[1:].name == tdi.name

def test_getitem(self):
Expand Down Expand Up @@ -230,7 +232,7 @@ def test_take_invalid_kwargs(self):

def test_take_equiv_getitem(self):
tds = ["1day 02:00:00", "1 day 04:00:00", "1 day 10:00:00"]
idx = timedelta_range(start="1d", end="2d", freq="h", name="idx")
idx = timedelta_range(start="1D", end="2D", freq="h", name="idx")
expected = TimedeltaIndex(tds, freq=None, name="idx")

taken1 = idx.take([2, 4, 10])
Expand Down Expand Up @@ -337,8 +339,10 @@ def test_contains_nonunique(self):

def test_contains(self):
# Checking for any NaT-like objects
# GH#13603
td = to_timedelta(range(5), unit="d") + offsets.Hour(1)
# GH#13603, GH#59051
msg = "'d' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg):
td = to_timedelta(range(5), unit="d") + offsets.Hour(1)
for v in [NaT, None, float("nan"), np.nan]:
assert v not in td

Expand Down
7 changes: 5 additions & 2 deletions pandas/tests/indexes/timedeltas/test_setops.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ def test_union_sort_false(self):
tm.assert_index_equal(result, expected)

def test_union_coverage(self):
idx = TimedeltaIndex(["3d", "1d", "2d"])
# GH#59051
msg = "'d' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg):
idx = TimedeltaIndex(["3d", "1d", "2d"])
ordered = TimedeltaIndex(idx.sort_values(), freq="infer")
result = ordered.union(idx)
tm.assert_index_equal(result, ordered)
Expand Down Expand Up @@ -70,7 +73,7 @@ def test_union_bug_1745(self):
tm.assert_index_equal(result, exp)

def test_union_bug_4564(self):
left = timedelta_range("1 day", "30d")
left = timedelta_range("1 day", "30D")
right = left + pd.offsets.Minute(15)

result = left.union(right)
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/indexing/test_categorical.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,13 +511,13 @@ def test_loc_and_at_with_categorical_index(self):
# pandas scalars
[Interval(1, 4), Interval(4, 6), Interval(6, 9)],
[Timestamp(2019, 1, 1), Timestamp(2019, 2, 1), Timestamp(2019, 3, 1)],
[Timedelta(1, "d"), Timedelta(2, "d"), Timedelta(3, "D")],
[Timedelta(1, "D"), Timedelta(2, "D"), Timedelta(3, "D")],
# pandas Integer arrays
*(pd.array([1, 2, 3], dtype=dtype) for dtype in tm.ALL_INT_EA_DTYPES),
# other pandas arrays
pd.IntervalIndex.from_breaks([1, 4, 6, 9]).array,
pd.date_range("2019-01-01", periods=3).array,
pd.timedelta_range(start="1d", periods=3).array,
pd.timedelta_range(start="1D", periods=3).array,
],
)
def test_loc_getitem_with_non_string_categories(self, idx_values, ordered):
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/io/sas/test_sas7bdat.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ def data_test_ix(request, dirpath):
fname = os.path.join(dirpath, f"test_sas7bdat_{i}.csv")
df = pd.read_csv(fname)
epoch = datetime(1960, 1, 1)
t1 = pd.to_timedelta(df["Column4"], unit="d")
t1 = pd.to_timedelta(df["Column4"], unit="D")
df["Column4"] = (epoch + t1).astype("M8[s]")
t2 = pd.to_timedelta(df["Column12"], unit="d")
t2 = pd.to_timedelta(df["Column12"], unit="D")
df["Column12"] = (epoch + t2).astype("M8[s]")
for k in range(df.shape[1]):
col = df.iloc[:, k]
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/reshape/merge/test_merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -1367,8 +1367,8 @@ def test_merge_two_empty_df_no_division_error(self):
),
),
(
TimedeltaIndex(["1d", "2d", "3d"]),
TimedeltaIndex(["1d", "2d", "3d", pd.NaT, pd.NaT, pd.NaT]),
TimedeltaIndex(["1D", "2D", "3D"]),
TimedeltaIndex(["1D", "2D", "3D", pd.NaT, pd.NaT, pd.NaT]),
),
],
)
Expand Down
Loading
Loading