diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 3096a13ac0767..df22a21196dab 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -540,6 +540,7 @@ Datetimelike - Bug in :func:`to_datetime` which does not replace the invalid argument with ``NaT`` when error is set to coerce (:issue:`26122`) - Bug in adding :class:`DateOffset` with nonzero month to :class:`DatetimeIndex` would raise ``ValueError`` (:issue:`26258`) - Bug in :func:`to_datetime` which raises unhandled ``OverflowError`` when called with mix of invalid dates and ``NaN`` values with ``format='%Y%m%d'`` and ``error='coerce'`` (:issue:`25512`) +- Bug in :meth:`isin` for datetimelike indexes; :class:`DatetimeIndex`, :class:`TimedeltaIndex` and :class:`PeriodIndex` where the ``levels`` parameter was ignored. (:issue:`26675`) - Bug in :func:`to_datetime` which raises ``TypeError`` for ``format='%Y%m%d'`` when called for invalid integer dates with length >= 6 digits with ``errors='ignore'`` - Bug when comparing a :class:`PeriodIndex` against a zero-dimensional numpy array (:issue:`26689`) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 092cec00228cd..fb92f918f9203 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -522,6 +522,9 @@ def isin(self, values, level=None): ------- is_contained : ndarray (boolean dtype) """ + if level is not None: + self._validate_index_level(level) + if not isinstance(values, type(self)): try: values = type(self)(values) diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 7e70d77ea70fc..1de20dc765655 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -1791,22 +1791,23 @@ def test_isin_level_kwarg(self, level, index): tm.assert_numpy_array_equal(expected, index.isin(values, level='foobar')) - @pytest.mark.parametrize("level", [1, 10, -2]) - @pytest.mark.parametrize("index", [ - Index(['qux', 'baz', 'foo', 'bar']), - # Float64Index overrides isin, so must be checked separately - Float64Index([1.0, 2.0, 3.0, 4.0])]) - def test_isin_level_kwarg_raises_bad_index(self, level, index): + @pytest.mark.parametrize("level", [2, 10, -3]) + def test_isin_level_kwarg_bad_level_raises(self, level, indices): + index = indices with pytest.raises(IndexError, match='Too many levels'): index.isin([], level=level) - @pytest.mark.parametrize("level", [1.0, 'foobar', 'xyzzy', np.nan]) - @pytest.mark.parametrize("index", [ - Index(['qux', 'baz', 'foo', 'bar']), - Float64Index([1.0, 2.0, 3.0, 4.0])]) - def test_isin_level_kwarg_raises_key(self, level, index): - with pytest.raises(KeyError, match='must be same as name'): - index.isin([], level=level) + @pytest.mark.parametrize("label", [1.0, 'foobar', 'xyzzy', np.nan]) + def test_isin_level_kwarg_bad_label_raises(self, label, indices): + index = indices + if isinstance(index, MultiIndex): + index = index.rename(['foo', 'bar']) + msg = "'Level {} not found'" + else: + index = index.rename('foo') + msg = r"'Level {} must be same as name \(foo\)'" + with pytest.raises(KeyError, match=msg.format(label)): + index.isin([], level=label) @pytest.mark.parametrize("empty", [[], Series(), np.array([])]) def test_isin_empty(self, empty):