diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 09cb024cbd95c..b5fbd277a3228 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -352,7 +352,7 @@ Deprecations - :class:`Index` methods ``&``, ``|``, and ``^`` behaving as the set operations :meth:`Index.intersection`, :meth:`Index.union`, and :meth:`Index.symmetric_difference`, respectively, are deprecated and in the future will behave as pointwise boolean operations matching :class:`Series` behavior. Use the named set methods instead (:issue:`36758`) - :meth:`Categorical.is_dtype_equal` and :meth:`CategoricalIndex.is_dtype_equal` are deprecated, will be removed in a future version (:issue:`37545`) - :meth:`Series.slice_shift` and :meth:`DataFrame.slice_shift` are deprecated, use :meth:`Series.shift` or :meth:`DataFrame.shift` instead (:issue:`37601`) - +- Partial slicing on unordered :class:`DatetimeIndexes` with keys, which are not in Index is deprecated and will be removed in a future version (:issue:`18531`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 9744eb0ecbb88..a1a1a83555f95 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -803,14 +803,25 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): end is None or isinstance(end, str) ): mask = np.array(True) + deprecation_mask = np.array(True) if start is not None: start_casted = self._maybe_cast_slice_bound(start, "left", kind) mask = start_casted <= self + deprecation_mask = start_casted == self if end is not None: end_casted = self._maybe_cast_slice_bound(end, "right", kind) mask = (self <= end_casted) & mask - + deprecation_mask = (end_casted == self) | deprecation_mask + + if not deprecation_mask.any(): + warnings.warn( + "Value based partial slicing on non-monotonic DatetimeIndexes " + "with non-existing keys is deprecated and will raise a " + "KeyError in a future Version.", + FutureWarning, + stacklevel=5, + ) indexer = mask.nonzero()[0][::step] if len(indexer) == len(self): return slice(None) diff --git a/pandas/tests/indexes/datetimes/test_partial_slicing.py b/pandas/tests/indexes/datetimes/test_partial_slicing.py index 57dc46e1fb415..05ee67eee0da5 100644 --- a/pandas/tests/indexes/datetimes/test_partial_slicing.py +++ b/pandas/tests/indexes/datetimes/test_partial_slicing.py @@ -312,17 +312,22 @@ def test_partial_slicing_with_multiindex(self): def test_partial_slice_doesnt_require_monotonicity(self): # For historical reasons. - s = Series(np.arange(10), date_range("2014-01-01", periods=10)) + ser = Series(np.arange(10), date_range("2014-01-01", periods=10)) - nonmonotonic = s[[3, 5, 4]] + nonmonotonic = ser[[3, 5, 4]] expected = nonmonotonic.iloc[:0] timestamp = Timestamp("2014-01-10") + with tm.assert_produces_warning(FutureWarning): + result = nonmonotonic["2014-01-10":] + tm.assert_series_equal(result, expected) - tm.assert_series_equal(nonmonotonic["2014-01-10":], expected) with pytest.raises(KeyError, match=r"Timestamp\('2014-01-10 00:00:00'\)"): nonmonotonic[timestamp:] - tm.assert_series_equal(nonmonotonic.loc["2014-01-10":], expected) + with tm.assert_produces_warning(FutureWarning): + result = nonmonotonic.loc["2014-01-10":] + tm.assert_series_equal(result, expected) + with pytest.raises(KeyError, match=r"Timestamp\('2014-01-10 00:00:00'\)"): nonmonotonic.loc[timestamp:] diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 9aab867df4b17..b45eddc3ac49c 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -1564,6 +1564,15 @@ def test_loc_getitem_slice_label_td64obj(self, start, stop, expected_slice): expected = ser.iloc[expected_slice] tm.assert_series_equal(result, expected) + @pytest.mark.parametrize("start", ["2018", "2020"]) + def test_loc_getitem_slice_unordered_dt_index(self, frame_or_series, start): + obj = frame_or_series( + [1, 2, 3], + index=[Timestamp("2016"), Timestamp("2019"), Timestamp("2017")], + ) + with tm.assert_produces_warning(FutureWarning): + obj.loc[start:"2022"] + class TestLocBooleanMask: def test_loc_setitem_bool_mask_timedeltaindex(self): diff --git a/pandas/tests/series/indexing/test_datetime.py b/pandas/tests/series/indexing/test_datetime.py index 44fb8dc519322..71ddf72562f36 100644 --- a/pandas/tests/series/indexing/test_datetime.py +++ b/pandas/tests/series/indexing/test_datetime.py @@ -526,7 +526,8 @@ def compare(slobj): tm.assert_series_equal(result, expected) compare(slice("2011-01-01", "2011-01-15")) - compare(slice("2010-12-30", "2011-01-15")) + with tm.assert_produces_warning(FutureWarning): + compare(slice("2010-12-30", "2011-01-15")) compare(slice("2011-01-01", "2011-01-16")) # partial ranges