Skip to content

API: change IntervalIndex.contains to work elementwise #17753

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 10 commits into from
Jul 1, 2019
14 changes: 6 additions & 8 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4015,13 +4015,6 @@ def is_type_compatible(self, kind):
>>> idx
Int64Index([1, 2, 3, 4], dtype='int64')

>>> idx.contains(2)
True
>>> idx.contains(6)
False

This is equivalent to:

>>> 2 in idx
True
>>> 6 in idx
Expand All @@ -4036,8 +4029,13 @@ def __contains__(self, key):
except (OverflowError, TypeError, ValueError):
return False

@Appender(_index_shared_docs['contains'] % _index_doc_kwargs)
def contains(self, key):
"""
Return a boolean indicating whether the provided key is in the index.

.. deprecated:: 0.25.0
Use ``key in index`` instead of ``index.contains(key)``.
"""
return key in self

def __hash__(self):
Expand Down
4 changes: 0 additions & 4 deletions pandas/core/indexes/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,6 @@ def __contains__(self, key):

return contains(self, key, container=self._engine)

@Appender(_index_shared_docs['contains'] % _index_doc_kwargs)
def contains(self, key):
return key in self

def __array__(self, dtype=None):
""" the array interface, return my values """
return np.array(self._data, dtype=dtype)
Expand Down
2 changes: 0 additions & 2 deletions pandas/core/indexes/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,6 @@ def __contains__(self, key):
except (KeyError, TypeError, ValueError):
return False

contains = __contains__

# Try to run function on index first, and then on elements of index
# Especially important for group-by functionality
def map(self, mapper, na_action=None):
Expand Down
20 changes: 9 additions & 11 deletions pandas/core/indexes/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,24 +293,22 @@ def __contains__(self, key):

def contains(self, key):
"""
Return a boolean indicating if the key is IN the index

We accept / allow keys to be not *just* actual
objects.
Return a boolean mask whether the key is contained in the Intervals
of the index.

Parameters
----------
key : int, float, Interval
key : scalar, Interval

Returns
-------
boolean
boolean array
"""
try:
self.get_loc(key)
return True
except KeyError:
return False
if isinstance(key, Interval):
raise TypeError('contains not defined for two intervals')

return ((self.left < key if self.open_left else self.left <= key) &
(key < self.right if self.open_right else key <= self.right))

@Appender(_interval_shared_docs['to_tuples'] % dict(
return_type="Index",
Expand Down
2 changes: 0 additions & 2 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,8 +922,6 @@ def __contains__(self, key):
except (LookupError, TypeError, ValueError):
return False

contains = __contains__

@Appender(_index_shared_docs['_shallow_copy'])
def _shallow_copy(self, values=None, **kwargs):
if values is not None:
Expand Down
2 changes: 0 additions & 2 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,6 @@ def __contains__(self, key):
except Exception:
return False

contains = __contains__

@cache_readonly
def _int64index(self):
return Int64Index._simple_new(self.asi8, name=self.name)
Expand Down
35 changes: 18 additions & 17 deletions pandas/tests/indexes/interval/test_interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -753,23 +753,26 @@ def test_contains(self):
assert Interval(3, 5) not in i
assert Interval(-1, 0, closed='left') not in i

# To be removed, replaced by test_interval_new.py (see #16316, #16386)
def testcontains(self):
def test_contains_method(self):
# can select values that are IN the range of a value
i = IntervalIndex.from_arrays([0, 1], [1, 2])

assert i.contains(0.1)
assert i.contains(0.5)
assert i.contains(1)
assert i.contains(Interval(0, 1))
assert i.contains(Interval(0, 2))
expected = np.array([False, False], dtype='bool')
actual = i.contains(0)
tm.assert_numpy_array_equal(actual, expected)
actual = i.contains(3)
tm.assert_numpy_array_equal(actual, expected)

# these overlaps completely
assert i.contains(Interval(0, 3))
assert i.contains(Interval(1, 3))
expected = np.array([True, False], dtype='bool')
actual = i.contains(0.5)
tm.assert_numpy_array_equal(actual, expected)
actual = i.contains(1)
tm.assert_numpy_array_equal(actual, expected)

assert not i.contains(20)
assert not i.contains(-20)
# __contains__ not implemented for "interval in interval", follow
# that for the contains method for now
with pytest.raises(TypeError):
i.contains(Interval(0, 1))

def test_dropna(self, closed):

Expand Down Expand Up @@ -939,11 +942,9 @@ def test_datetime(self, tz):
assert iv_false not in index

# .contains does check individual points
assert not index.contains(Timestamp('2000-01-01', tz=tz))
assert index.contains(Timestamp('2000-01-01T12', tz=tz))
assert index.contains(Timestamp('2000-01-02', tz=tz))
assert index.contains(iv_true)
assert not index.contains(iv_false)
assert not index.contains(Timestamp('2000-01-01', tz=tz)).any()
assert index.contains(Timestamp('2000-01-01T12', tz=tz)).any()
assert index.contains(Timestamp('2000-01-02', tz=tz)).any()

# test get_indexer
start = Timestamp('1999-12-31T12:00', tz=tz)
Expand Down