From 51165bf67409f878d7bf95b175c6e282455fc867 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 13 Jun 2019 15:58:01 -0700 Subject: [PATCH 1/2] deprecate Timedelta.resolution --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/_libs/tslibs/timedeltas.pyx | 53 ++++++++++++++++++- pandas/core/indexes/timedeltas.py | 4 +- pandas/tests/scalar/test_nat.py | 4 +- .../tests/scalar/timedelta/test_timedelta.py | 16 ++++++ 5 files changed, 72 insertions(+), 7 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index fd47ca14dc788..06cbed8ac4700 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -501,7 +501,7 @@ Other Deprecations Use the public attributes :attr:`~RangeIndex.start`, :attr:`~RangeIndex.stop` and :attr:`~RangeIndex.step` instead (:issue:`26581`). - The :meth:`Series.ftype`, :meth:`Series.ftypes` and :meth:`DataFrame.ftypes` methods are deprecated and will be removed in a future version. Instead, use :meth:`Series.dtype` and :meth:`DataFrame.dtypes` (:issue:`26705`). - +- :meth:`Timedelta.resolution` is deprecated and replaced with :meth:`Timedelta.reso_str`. In a future version, :meth:`Timedelta.resolution` will be changed to behave like the standard library :attr:`timedelta.resolution` (:issue:`21344`) .. _whatsnew_0250.prior_deprecations: diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index ad60165e98d4f..fc989b40a51a0 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -950,7 +950,7 @@ cdef class _Timedelta(timedelta): return np.int64(self.value).view('m8[ns]') @property - def resolution(self): + def reso_str(self): """ Return a string representing the lowest timedelta resolution. @@ -991,7 +991,6 @@ cdef class _Timedelta(timedelta): >>> td.resolution 'U' """ - self._ensure_components() if self._ns: return "N" @@ -1008,6 +1007,56 @@ cdef class _Timedelta(timedelta): else: return "D" + @property + def resolution(self): + """ + Return a string representing the lowest timedelta resolution. + + Each timedelta has a defined resolution that represents the lowest OR + most granular level of precision. Each level of resolution is + represented by a short string as defined below: + + Resolution: Return value + + * Days: 'D' + * Hours: 'H' + * Minutes: 'T' + * Seconds: 'S' + * Milliseconds: 'L' + * Microseconds: 'U' + * Nanoseconds: 'N' + + Returns + ------- + str + Timedelta resolution. + + Examples + -------- + >>> td = pd.Timedelta('1 days 2 min 3 us 42 ns') + >>> td.resolution + 'N' + + >>> td = pd.Timedelta('1 days 2 min 3 us') + >>> td.resolution + 'U' + + >>> td = pd.Timedelta('2 min 3 s') + >>> td.resolution + 'S' + + >>> td = pd.Timedelta(36, unit='us') + >>> td.resolution + 'U' + """ + # See GH#21344 + warnings.warn("Timedelta.resolution is deprecated, in a future " + "version will behave like the standard library " + "datetime.timedelta.resolution attribute. " + "Use Timedelta.reso_str instead.", + FutureWarning) + return self.reso_str + @property def nanoseconds(self): """ diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index f5362c0b6bb5d..e95203c0d9916 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -551,11 +551,11 @@ def _maybe_cast_slice_bound(self, label, side, kind): if isinstance(label, str): parsed = Timedelta(label) - lbound = parsed.round(parsed.resolution) + lbound = parsed.round(parsed.reso_str) if side == 'left': return lbound else: - return (lbound + to_offset(parsed.resolution) - + return (lbound + to_offset(parsed.reso_str) - Timedelta(1, 'ns')) elif ((is_integer(label) or is_float(label)) and not is_timedelta64_dtype(label)): diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index 0ae4d107d85bd..a05d1796f9791 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -142,8 +142,8 @@ def test_nat_iso_format(get_nat): @pytest.mark.parametrize("klass,expected", [ (Timestamp, ["freqstr", "normalize", "to_julian_date", "to_period", "tz"]), - (Timedelta, ["components", "delta", "is_populated", "to_pytimedelta", - "to_timedelta64", "view"]) + (Timedelta, ["components", "delta", "is_populated", "reso_str", + "to_pytimedelta", "to_timedelta64", "view"]) ]) def test_missing_public_nat_methods(klass, expected): # see gh-17327 diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index 57b3705640202..65c4f6d0609e2 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -742,6 +742,22 @@ def test_components(self): assert not result.iloc[0].isna().all() assert result.iloc[1].isna().all() + def test_reso_str(self): + assert Timedelta(days=1).reso_str == 'D' + assert Timedelta(days=1, hours=6).reso_str == 'H' + assert Timedelta(days=1, minutes=6).reso_str == 'T' + assert Timedelta(days=1, seconds=6).reso_str == 'S' + assert Timedelta(days=1, milliseconds=6).reso_str == 'L' + assert Timedelta(days=1, microseconds=6).reso_str == 'U' + assert Timedelta(days=1, nanoseconds=6).reso_str == 'N' + + def test_resolution_deprecated(self): + # GH#21344 + td = Timedelta(days=4, hours=3) + with tm.assert_produces_warning(FutureWarning) as w: + td.resolution + assert "Use Timedelta.reso_str instead" in str(w[0].message) + @pytest.mark.parametrize('value, expected', [ (Timedelta('10S'), True), From 9a67940ee208155e6c86aacd7f78e78e763bb2a9 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 16 Jun 2019 13:48:36 -0700 Subject: [PATCH 2/2] change reso_str --> resolution_string --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/_libs/tslibs/timedeltas.pyx | 6 +++--- pandas/core/indexes/timedeltas.py | 4 ++-- pandas/tests/scalar/test_nat.py | 2 +- .../tests/scalar/timedelta/test_timedelta.py | 18 +++++++++--------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 06cbed8ac4700..d3851662c34ec 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -501,7 +501,7 @@ Other Deprecations Use the public attributes :attr:`~RangeIndex.start`, :attr:`~RangeIndex.stop` and :attr:`~RangeIndex.step` instead (:issue:`26581`). - The :meth:`Series.ftype`, :meth:`Series.ftypes` and :meth:`DataFrame.ftypes` methods are deprecated and will be removed in a future version. Instead, use :meth:`Series.dtype` and :meth:`DataFrame.dtypes` (:issue:`26705`). -- :meth:`Timedelta.resolution` is deprecated and replaced with :meth:`Timedelta.reso_str`. In a future version, :meth:`Timedelta.resolution` will be changed to behave like the standard library :attr:`timedelta.resolution` (:issue:`21344`) +- :meth:`Timedelta.resolution` is deprecated and replaced with :meth:`Timedelta.resolution_string`. In a future version, :meth:`Timedelta.resolution` will be changed to behave like the standard library :attr:`timedelta.resolution` (:issue:`21344`) .. _whatsnew_0250.prior_deprecations: diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index fc989b40a51a0..6a32553fe2d38 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -950,7 +950,7 @@ cdef class _Timedelta(timedelta): return np.int64(self.value).view('m8[ns]') @property - def reso_str(self): + def resolution_string(self): """ Return a string representing the lowest timedelta resolution. @@ -1053,9 +1053,9 @@ cdef class _Timedelta(timedelta): warnings.warn("Timedelta.resolution is deprecated, in a future " "version will behave like the standard library " "datetime.timedelta.resolution attribute. " - "Use Timedelta.reso_str instead.", + "Use Timedelta.resolution_string instead.", FutureWarning) - return self.reso_str + return self.resolution_string @property def nanoseconds(self): diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index e95203c0d9916..ba5507fa71e8c 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -551,11 +551,11 @@ def _maybe_cast_slice_bound(self, label, side, kind): if isinstance(label, str): parsed = Timedelta(label) - lbound = parsed.round(parsed.reso_str) + lbound = parsed.round(parsed.resolution_string) if side == 'left': return lbound else: - return (lbound + to_offset(parsed.reso_str) - + return (lbound + to_offset(parsed.resolution_string) - Timedelta(1, 'ns')) elif ((is_integer(label) or is_float(label)) and not is_timedelta64_dtype(label)): diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index a05d1796f9791..19426c3bf3ffb 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -142,7 +142,7 @@ def test_nat_iso_format(get_nat): @pytest.mark.parametrize("klass,expected", [ (Timestamp, ["freqstr", "normalize", "to_julian_date", "to_period", "tz"]), - (Timedelta, ["components", "delta", "is_populated", "reso_str", + (Timedelta, ["components", "delta", "is_populated", "resolution_string", "to_pytimedelta", "to_timedelta64", "view"]) ]) def test_missing_public_nat_methods(klass, expected): diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index 65c4f6d0609e2..f10876531e66a 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -742,21 +742,21 @@ def test_components(self): assert not result.iloc[0].isna().all() assert result.iloc[1].isna().all() - def test_reso_str(self): - assert Timedelta(days=1).reso_str == 'D' - assert Timedelta(days=1, hours=6).reso_str == 'H' - assert Timedelta(days=1, minutes=6).reso_str == 'T' - assert Timedelta(days=1, seconds=6).reso_str == 'S' - assert Timedelta(days=1, milliseconds=6).reso_str == 'L' - assert Timedelta(days=1, microseconds=6).reso_str == 'U' - assert Timedelta(days=1, nanoseconds=6).reso_str == 'N' + def test_resolution_string(self): + assert Timedelta(days=1).resolution_string == 'D' + assert Timedelta(days=1, hours=6).resolution_string == 'H' + assert Timedelta(days=1, minutes=6).resolution_string == 'T' + assert Timedelta(days=1, seconds=6).resolution_string == 'S' + assert Timedelta(days=1, milliseconds=6).resolution_string == 'L' + assert Timedelta(days=1, microseconds=6).resolution_string == 'U' + assert Timedelta(days=1, nanoseconds=6).resolution_string == 'N' def test_resolution_deprecated(self): # GH#21344 td = Timedelta(days=4, hours=3) with tm.assert_produces_warning(FutureWarning) as w: td.resolution - assert "Use Timedelta.reso_str instead" in str(w[0].message) + assert "Use Timedelta.resolution_string instead" in str(w[0].message) @pytest.mark.parametrize('value, expected', [