diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 385b7e487d4c5..1edd98e980a2d 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -3566,18 +3566,19 @@ def union(self, other, sort=None): """ self._validate_sort_keyword(sort) self._assert_can_do_setop(other) - other, result_names = self._convert_can_do_setop(other) + other, _ = self._convert_can_do_setop(other) - if len(other) == 0 or self.equals(other): - return self.rename(result_names) + if not len(other) or self.equals(other): + return self._get_reconciled_name_object(other) + + if not len(self): + return other._get_reconciled_name_object(self) 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): raise NotImplementedError( "Can only union MultiIndex with MultiIndex or Index of tuples, " @@ -3608,10 +3609,10 @@ def _maybe_match_names(self, other): """ Try to find common names to attach to the result of an operation between a and b. Return a consensus list of names if they match at least partly - or None if they have completely different names. + or list of None if they have completely different names. """ if len(self.names) != len(other.names): - return None + return [None] * len(self.names) names = [] for a_name, b_name in zip(self.names, other.names): if a_name == b_name: diff --git a/pandas/tests/indexes/multi/test_setops.py b/pandas/tests/indexes/multi/test_setops.py index f9fc425e46696..a26eb793afe7e 100644 --- a/pandas/tests/indexes/multi/test_setops.py +++ b/pandas/tests/indexes/multi/test_setops.py @@ -387,6 +387,15 @@ def test_union_non_object_dtype_raises(): mi.union(idx) +def test_union_empty_self_different_names(): + # GH#38423 + mi = MultiIndex.from_arrays([[]]) + mi2 = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["a", "b"]) + result = mi.union(mi2) + expected = MultiIndex.from_arrays([[1, 2], [3, 4]]) + tm.assert_index_equal(result, expected) + + @pytest.mark.parametrize( "method", ["union", "intersection", "difference", "symmetric_difference"] ) @@ -424,12 +433,12 @@ def test_intersect_with_duplicates(tuples, exp_tuples): @pytest.mark.parametrize( "data, names, expected", [ - ((1,), None, None), - ((1,), ["a"], None), - ((1,), ["b"], None), + ((1,), None, [None, None]), + ((1,), ["a"], [None, None]), + ((1,), ["b"], [None, None]), ((1, 2), ["c", "d"], [None, None]), ((1, 2), ["b", "a"], [None, None]), - ((1, 2, 3), ["a", "b", "c"], None), + ((1, 2, 3), ["a", "b", "c"], [None, None]), ((1, 2), ["a", "c"], ["a", None]), ((1, 2), ["c", "b"], [None, "b"]), ((1, 2), ["a", "b"], ["a", "b"]),