Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v2.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,11 @@ Removal of prior version deprecations/changes
- Remove :meth:`DataFrameGroupBy.pad` and :meth:`DataFrameGroupBy.backfill` (:issue:`45076`)
- Remove ``numpy`` argument from :func:`read_json` (:issue:`30636`)
- Disallow passing abbreviations for ``orient`` in :meth:`DataFrame.to_dict` (:issue:`32516`)
- Disallow partial slicing on an unordered :class:`DatetimeIndex` with keys, which are not in Index. This now raises a ``KeyError`` (:issue:`18531`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"keys, which are not in Index" -> "keys which are not in the Index"?
"unordered" -> "non-monotonic"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Updated + greenish

- Removed ``get_offset`` in favor of :func:`to_offset` (:issue:`30340`)
- Removed the ``warn`` keyword in :func:`infer_freq` (:issue:`45947`)
- Removed the ``include_start`` and ``include_end`` arguments in :meth:`DataFrame.between_time` in favor of ``inclusive`` (:issue:`43248`)
- Removed the ``closed`` argument in :meth:`date_range` and :meth:`bdate_range` in favor of ``inclusive`` argument (:issue:`40245`)
- Removed the ``center`` keyword in :meth:`DataFrame.expanding` (:issue:`20647`)
- Removed the ``truediv`` keyword from :func:`eval` (:issue:`29812`)
- Removed the ``pandas.datetime`` submodule (:issue:`30489`)
Expand Down
61 changes: 8 additions & 53 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from typing import (
TYPE_CHECKING,
Hashable,
Literal,
)
import warnings

Expand All @@ -37,7 +36,6 @@
DtypeObj,
Frequency,
IntervalClosedType,
IntervalLeftRight,
TimeAmbiguous,
TimeNonexistent,
npt,
Expand All @@ -46,7 +44,6 @@
cache_readonly,
doc,
)
from pandas.util._exceptions import find_stack_level

from pandas.core.dtypes.common import (
is_datetime64_dtype,
Expand Down Expand Up @@ -699,24 +696,21 @@ def check_str_or_none(point) -> bool:
return Index.slice_indexer(self, start, end, step)

mask = np.array(True)
deprecation_mask = np.array(True)
raise_mask = np.array(True)
if start is not None:
start_casted = self._maybe_cast_slice_bound(start, "left")
mask = start_casted <= self
deprecation_mask = start_casted == self
raise_mask = start_casted == self

if end is not None:
end_casted = self._maybe_cast_slice_bound(end, "right")
mask = (self <= end_casted) & mask
deprecation_mask = (end_casted == self) | deprecation_mask
raise_mask = (end_casted == self) | raise_mask

if not deprecation_mask.any():
warnings.warn(
if not raise_mask.any():
raise KeyError(
"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=find_stack_level(),
"with non-existing keys is not allowed.",
)
indexer = mask.nonzero()[0][::step]
if len(indexer) == len(self):
Expand Down Expand Up @@ -829,8 +823,7 @@ def date_range(
tz=None,
normalize: bool = False,
name: Hashable = None,
closed: Literal["left", "right"] | None | lib.NoDefault = lib.no_default,
inclusive: IntervalClosedType | None = None,
inclusive: IntervalClosedType = "both",
**kwargs,
) -> DatetimeIndex:
"""
Expand Down Expand Up @@ -865,13 +858,6 @@ def date_range(
Normalize start/end dates to midnight before generating date range.
name : str, default None
Name of the resulting DatetimeIndex.
closed : {None, 'left', 'right'}, optional
Make the interval closed with respect to the given frequency to
the 'left', 'right', or both sides (None, the default).

.. deprecated:: 1.4.0
Argument `closed` has been deprecated to standardize boundary inputs.
Use `inclusive` instead, to set each bound as closed or open.
inclusive : {"both", "neither", "left", "right"}, default "both"
Include boundaries; Whether to set each bound as closed or open.

Expand Down Expand Up @@ -987,28 +973,6 @@ def date_range(
DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04'],
dtype='datetime64[ns]', freq='D')
"""
if inclusive is not None and closed is not lib.no_default:
raise ValueError(
"Deprecated argument `closed` cannot be passed"
"if argument `inclusive` is not None"
)
if closed is not lib.no_default:
warnings.warn(
"Argument `closed` is deprecated in favor of `inclusive`.",
FutureWarning,
stacklevel=find_stack_level(),
)
if closed is None:
inclusive = "both"
elif closed in ("left", "right"):
inclusive = closed
else:
raise ValueError(
"Argument `closed` has to be either 'left', 'right' or None"
)
elif inclusive is None:
inclusive = "both"

if freq is None and com.any_none(periods, start, end):
freq = "D"

Expand All @@ -1035,8 +999,7 @@ def bdate_range(
name: Hashable = None,
weekmask=None,
holidays=None,
closed: IntervalLeftRight | lib.NoDefault | None = lib.no_default,
inclusive: IntervalClosedType | None = None,
inclusive: IntervalClosedType = "both",
**kwargs,
) -> DatetimeIndex:
"""
Expand Down Expand Up @@ -1068,13 +1031,6 @@ def bdate_range(
Dates to exclude from the set of valid business days, passed to
``numpy.busdaycalendar``, only used when custom frequency strings
are passed.
closed : str, default None
Make the interval closed with respect to the given frequency to
the 'left', 'right', or both sides (None).

.. deprecated:: 1.4.0
Argument `closed` has been deprecated to standardize boundary inputs.
Use `inclusive` instead, to set each bound as closed or open.
inclusive : {"both", "neither", "left", "right"}, default "both"
Include boundaries; Whether to set each bound as closed or open.

Expand Down Expand Up @@ -1131,7 +1087,6 @@ def bdate_range(
tz=tz,
normalize=normalize,
name=name,
closed=closed,
inclusive=inclusive,
**kwargs,
)
Expand Down
19 changes: 10 additions & 9 deletions pandas/tests/indexes/datetimes/test_partial_slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,23 +376,24 @@ def test_partial_slicing_with_multiindex_series(self):
result = df2.loc[Timestamp("2000-1-4")]
tm.assert_frame_equal(result, expected)

def test_partial_slice_doesnt_require_monotonicity(self):
# For historical reasons.
def test_partial_slice_requires_monotonicity(self):
# Disallowed since 2.0 (GH 37819)
ser = Series(np.arange(10), date_range("2014-01-01", periods=10))

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)
with pytest.raises(
KeyError, match="Value based partial slicing on non-monotonic"
):
nonmonotonic["2014-01-10":]

with pytest.raises(KeyError, match=r"Timestamp\('2014-01-10 00:00:00'\)"):
nonmonotonic[timestamp:]

with tm.assert_produces_warning(FutureWarning):
result = nonmonotonic.loc["2014-01-10":]
tm.assert_series_equal(result, expected)
with pytest.raises(
KeyError, match="Value based partial slicing on non-monotonic"
):
nonmonotonic.loc["2014-01-10":]

with pytest.raises(KeyError, match=r"Timestamp\('2014-01-10 00:00:00'\)"):
nonmonotonic.loc[timestamp:]
Expand Down
4 changes: 3 additions & 1 deletion pandas/tests/indexing/test_loc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2460,7 +2460,9 @@ def test_loc_getitem_slice_unordered_dt_index(self, frame_or_series, start):
[1, 2, 3],
index=[Timestamp("2016"), Timestamp("2019"), Timestamp("2017")],
)
with tm.assert_produces_warning(FutureWarning):
with pytest.raises(
KeyError, match="Value based partial slicing on non-monotonic"
):
obj.loc[start:"2022"]

@pytest.mark.parametrize("value", [1, 1.5])
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/series/indexing/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ def compare(slobj):
tm.assert_series_equal(result, expected)

compare(slice("2011-01-01", "2011-01-15"))
with tm.assert_produces_warning(FutureWarning):
with pytest.raises(KeyError, match="Value based partial slicing on non-monotonic"):
compare(slice("2010-12-30", "2011-01-15"))
compare(slice("2011-01-01", "2011-01-16"))

Expand Down