From ba032fe0741a9d089f1beb5fe9bb5d324f768bd6 Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 3 Dec 2020 17:43:45 -0800 Subject: [PATCH 1/2] BUG: IntervalIndex.union with mismatched dtypes both empty --- pandas/core/indexes/base.py | 2 +- pandas/core/indexes/interval.py | 6 +++++- pandas/core/indexes/multi.py | 5 +++++ pandas/tests/indexes/interval/test_setops.py | 6 ++++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 3778f234bdb4a..e93849f5cb537 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -2694,7 +2694,7 @@ def union(self, other, sort=None): """ self._validate_sort_keyword(sort) self._assert_can_do_setop(other) - other = ensure_index(other) + other, result_name = self._convert_can_do_setop(other) if not self._can_union_without_object_cast(other): return self._union_incompatible_dtypes(other, sort=sort) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 18be4bf225da5..9bd6c08e17b2d 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -1051,6 +1051,10 @@ def _intersection_non_unique(self, other: "IntervalIndex") -> "IntervalIndex": def _setop(op_name: str, sort=None): def func(self, other, sort=sort): + # At this point we are assured + # isinstance(other, IntervalIndex) + # other.closed == self.closed + result = getattr(self._multiindex, op_name)(other._multiindex, sort=sort) result_name = get_op_result_name(self, other) @@ -1065,7 +1069,7 @@ def func(self, other, sort=sort): func.__name__ = op_name return setop_check(func) - union = _setop("union") + _union = _setop("union") difference = _setop("difference") symmetric_difference = _setop("symmetric_difference") diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 80f4e3457b7a2..507d7d2a4dcc7 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -3569,6 +3569,11 @@ def union(self, other, sort=None): if len(other) == 0 or self.equals(other): return self.rename(result_names) + return self._union(other, sort=sort) + + def _union(self, other, sort): + other, result_names = self._convert_can_do_setop(other) + # TODO: Index.union returns other when `len(self)` is 0. if not is_object_dtype(other.dtype): diff --git a/pandas/tests/indexes/interval/test_setops.py b/pandas/tests/indexes/interval/test_setops.py index 0b94d70367b4d..0ef833bb93ded 100644 --- a/pandas/tests/indexes/interval/test_setops.py +++ b/pandas/tests/indexes/interval/test_setops.py @@ -32,15 +32,17 @@ def test_union(self, closed, sort): tm.assert_index_equal(index.union(index, sort=sort), index) tm.assert_index_equal(index.union(index[:1], sort=sort), index) + def test_union_empty_result(self, closed, sort): # GH 19101: empty result, same dtype index = empty_index(dtype="int64", closed=closed) result = index.union(index, sort=sort) tm.assert_index_equal(result, index) - # GH 19101: empty result, different dtypes + # GH 19101: empty result, different dtypes -> common dtype is object other = empty_index(dtype="float64", closed=closed) result = index.union(other, sort=sort) - tm.assert_index_equal(result, index) + expected = Index([], dtype=object) + tm.assert_index_equal(result, expected) def test_intersection(self, closed, sort): index = monotonic_index(0, 11, closed=closed) From 6301e879b0720daed0efcdf83f04610f65181238 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 4 Dec 2020 16:03:34 -0800 Subject: [PATCH 2/2] whatsnew --- doc/source/whatsnew/v1.2.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 94eb9a0b3fc58..cb4d04d4443e7 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -831,6 +831,7 @@ Other - Bug in :meth:`Index.difference` failing to set the correct name on the returned :class:`Index` in some corner cases (:issue:`38268`) - Bug in :meth:`Index.union` behaving differently depending on whether operand is an :class:`Index` or other list-like (:issue:`36384`) - Bug in :meth:`Index.intersection` with non-matching numeric dtypes casting to ``object`` dtype instead of minimal common dtype (:issue:`38122`) +- Bug in :meth:`IntervalIndex.intersection` returning an incorrectly-typed :class:`Index` when empty (:issue:`38282`) - Passing an array with 2 or more dimensions to the :class:`Series` constructor now raises the more specific ``ValueError`` rather than a bare ``Exception`` (:issue:`35744`) - Bug in ``dir`` where ``dir(obj)`` wouldn't show attributes defined on the instance for pandas objects (:issue:`37173`) - Bug in :meth:`Index.drop` raising ``InvalidIndexError`` when index has duplicates (:issue:`38051`)