From fc52a6cdecc60cda9cd04354be7ed6cda25ca15c Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 11 Mar 2020 17:16:50 -0700 Subject: [PATCH 1/2] MultiIndex union/intersection with non-object other --- pandas/core/indexes/multi.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 0bb88145646ed..4550e7b3fcb8c 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -3235,9 +3235,13 @@ def union(self, other, sort=None): # TODO: Index.union returns other when `len(self)` is 0. - uniq_tuples = lib.fast_unique_multiple( - [self._values, other._ndarray_values], sort=sort - ) + if not is_object_dtype(other.dtype): + raise NotImplementedError( + "Can only union MultiIndex with MultiIndex or Index of tuples, " + "try mi.to_flat_index().union(other) instead." + ) + + uniq_tuples = lib.fast_unique_multiple([self._values, other._values], sort=sort) return MultiIndex.from_arrays( zip(*uniq_tuples), sortorder=0, names=result_names @@ -3271,8 +3275,18 @@ def intersection(self, other, sort=False): if self.equals(other): return self + if not is_object_dtype(other.dtype): + # The intersection is empty + # TODO: we have no tests that get here + return MultiIndex( + levels=self.levels, + codes=[[]] * self.nlevels, + names=result_names, + verify_integrity=False, + ) + lvals = self._values - rvals = other._ndarray_values + rvals = other._values uniq_tuples = None # flag whether _inner_indexer was succesful if self.is_monotonic and other.is_monotonic: From 982cd95dc8c04fec637002aa94c5fa3b0e4dde34 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 14 Mar 2020 09:35:53 -0700 Subject: [PATCH 2/2] test --- pandas/tests/indexes/multi/test_setops.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pandas/tests/indexes/multi/test_setops.py b/pandas/tests/indexes/multi/test_setops.py index d7d0ff4c411aa..b24f56afee376 100644 --- a/pandas/tests/indexes/multi/test_setops.py +++ b/pandas/tests/indexes/multi/test_setops.py @@ -246,6 +246,7 @@ def test_union(idx, sort): the_union = idx.union(idx[:0], sort=sort) assert the_union is idx + # FIXME: dont leave commented-out # won't work in python 3 # tuples = _index.values # result = _index[:4] | tuples[4:] @@ -282,6 +283,7 @@ def test_intersection(idx, sort): expected = idx[:0] assert empty.equals(expected) + # FIXME: dont leave commented-out # can't do in python 3 # tuples = _index.values # result = _index & tuples @@ -351,6 +353,17 @@ def test_union_sort_other_incomparable_sort(): idx.union(idx[:1], sort=True) +def test_union_non_object_dtype_raises(): + # GH#32646 raise NotImplementedError instead of less-informative error + mi = pd.MultiIndex.from_product([["a", "b"], [1, 2]]) + + idx = mi.levels[1] + + msg = "Can only union MultiIndex with MultiIndex or Index of tuples" + with pytest.raises(NotImplementedError, match=msg): + mi.union(idx) + + @pytest.mark.parametrize( "method", ["union", "intersection", "difference", "symmetric_difference"] )