Skip to content

REF: de-duplicate IntervalIndex compatiblity checks #37916

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 1 commit into from
Nov 18, 2020
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
3 changes: 2 additions & 1 deletion pandas/_libs/interval.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ cdef class IntervalMixin:
return (self.right == self.left) & (self.closed != 'both')

def _check_closed_matches(self, other, name='other'):
"""Check if the closed attribute of `other` matches.
"""
Check if the closed attribute of `other` matches.

Note that 'left' and 'right' are considered different from 'both'.

Expand Down
39 changes: 19 additions & 20 deletions pandas/core/indexes/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,13 @@ def wrapped(self, other, sort=False):
if op_name in ("difference",):
result = result.astype(self.dtype)
return result
elif self.closed != other.closed:
raise ValueError(
"can only do set operations between two IntervalIndex "
"objects that are closed on the same side"
)

# GH 19016: ensure set op will not return a prohibited dtype
subtypes = [self.dtype.subtype, other.dtype.subtype]
common_subtype = find_common_type(subtypes)
if is_object_dtype(common_subtype):
if self._is_non_comparable_own_type(other):
# GH#19016: ensure set op will not return a prohibited dtype
raise TypeError(
f"can only do {op_name} between two IntervalIndex "
"objects that have compatible dtypes"
"can only do set operations between two IntervalIndex "
"objects that are closed on the same side "
"and have compatible dtypes"
)

return method(self, other, sort)
Expand Down Expand Up @@ -717,11 +711,8 @@ def get_indexer(
if self.equals(target_as_index):
return np.arange(len(self), dtype="intp")

# different closed or incompatible subtype -> no matches
common_subtype = find_common_type(
[self.dtype.subtype, target_as_index.dtype.subtype]
)
if self.closed != target_as_index.closed or is_object_dtype(common_subtype):
if self._is_non_comparable_own_type(target_as_index):
# different closed or incompatible subtype -> no matches
return np.repeat(np.intp(-1), len(target_as_index))

# non-overlapping -> at most one match per interval in target_as_index
Expand Down Expand Up @@ -763,10 +754,8 @@ def get_indexer_non_unique(

# check that target_as_index IntervalIndex is compatible
if isinstance(target_as_index, IntervalIndex):
common_subtype = find_common_type(
[self.dtype.subtype, target_as_index.dtype.subtype]
)
if self.closed != target_as_index.closed or is_object_dtype(common_subtype):

if self._is_non_comparable_own_type(target_as_index):
# different closed or incompatible subtype -> no matches
return (
np.repeat(-1, len(target_as_index)),
Expand Down Expand Up @@ -837,6 +826,16 @@ def _convert_list_indexer(self, keyarr):

return locs

def _is_non_comparable_own_type(self, other: "IntervalIndex") -> bool:
# different closed or incompatible subtype -> no matches

# TODO: once closed is part of IntervalDtype, we can just define
# is_comparable_dtype GH#19371
if self.closed != other.closed:
return True
common_subtype = find_common_type([self.dtype.subtype, other.dtype.subtype])
return is_object_dtype(common_subtype)

# --------------------------------------------------------------------

@cache_readonly
Expand Down
8 changes: 4 additions & 4 deletions pandas/tests/indexes/interval/test_setops.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,18 @@ def test_set_incompatible_types(self, closed, op_name, sort):
# mixed closed
msg = (
"can only do set operations between two IntervalIndex objects "
"that are closed on the same side"
"that are closed on the same side and have compatible dtypes"
)
for other_closed in {"right", "left", "both", "neither"} - {closed}:
other = monotonic_index(0, 11, closed=other_closed)
with pytest.raises(ValueError, match=msg):
with pytest.raises(TypeError, match=msg):
set_op(other, sort=sort)

# GH 19016: incompatible dtypes
other = interval_range(Timestamp("20180101"), periods=9, closed=closed)
msg = (
f"can only do {op_name} between two IntervalIndex objects that have "
"compatible dtypes"
"can only do set operations between two IntervalIndex objects "
"that are closed on the same side and have compatible dtypes"
)
with pytest.raises(TypeError, match=msg):
set_op(other, sort=sort)