Skip to content

DEPR: Enforce deprecations in indexes/datetimes.py #49607

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

Merged
merged 4 commits into from
Nov 10, 2022
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
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 non-monotonic :class:`DatetimeIndex` with keys which are not in Index. This now raises a ``KeyError`` (:issue:`18531`)
- 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
13 changes: 4 additions & 9 deletions pandas/tests/frame/indexing/test_getitem.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,15 +455,10 @@ def test_getitem_datetime_slice(self):
]
),
)
with tm.assert_produces_warning(FutureWarning):
result = df["2011-01-01":"2011-11-01"]
expected = DataFrame(
{"a": 0},
index=DatetimeIndex(
["11.01.2011 22:00", "11.01.2011 23:00", "2011-01-13 00:00"]
),
)
tm.assert_frame_equal(result, expected)
with pytest.raises(
KeyError, match="Value based partial slicing on non-monotonic"
):
df["2011-01-01":"2011-11-01"]


class TestGetitemDeprecatedIndexers:
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