diff --git a/doc/source/whatsnew/v1.6.0.rst b/doc/source/whatsnew/v1.6.0.rst index eac5e5d3a0f52..83dfacb46784b 100644 --- a/doc/source/whatsnew/v1.6.0.rst +++ b/doc/source/whatsnew/v1.6.0.rst @@ -161,7 +161,7 @@ Missing MultiIndex ^^^^^^^^^^ -- +- Bug in :meth:`MultiIndex.append` not checking names for equality (:issue:`48288`) - I/O diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 5d751e173fc13..d8455b9014f75 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2204,19 +2204,23 @@ def append(self, other): if all( (isinstance(o, MultiIndex) and o.nlevels >= self.nlevels) for o in other ): - arrays = [] + arrays, names = [], [] for i in range(self.nlevels): label = self._get_level_values(i) appended = [o._get_level_values(i) for o in other] arrays.append(label.append(appended)) - return MultiIndex.from_arrays(arrays, names=self.names) + single_label_name = all(label.name == x.name for x in appended) + names.append(label.name if single_label_name else None) + return MultiIndex.from_arrays(arrays, names=names) to_concat = (self._values,) + tuple(k._values for k in other) new_tuples = np.concatenate(to_concat) # if all(isinstance(x, MultiIndex) for x in other): try: - return MultiIndex.from_tuples(new_tuples, names=self.names) + # We only get here if other contains at least one index with tuples, + # setting names to None automatically + return MultiIndex.from_tuples(new_tuples) except (TypeError, IndexError): return Index._with_infer(new_tuples) diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index 867835ef7f0a3..4f3abb922c6df 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -391,7 +391,13 @@ def _all_key(key): # GH31016 this is to calculate margin for each group, and assign # corresponded key as index transformed_piece = DataFrame(piece.apply(aggfunc)).T - transformed_piece.index = Index([all_key], name=piece.index.name) + if isinstance(piece.index, MultiIndex): + # We are adding an empty level + transformed_piece.index = MultiIndex.from_tuples( + [all_key], names=piece.index.names + [None] + ) + else: + transformed_piece.index = Index([all_key], name=piece.index.name) # append piece for margin into table_piece table_pieces.append(transformed_piece) diff --git a/pandas/tests/indexes/multi/test_reshape.py b/pandas/tests/indexes/multi/test_reshape.py index b1deec12b1adb..f4b845be2709c 100644 --- a/pandas/tests/indexes/multi/test_reshape.py +++ b/pandas/tests/indexes/multi/test_reshape.py @@ -150,6 +150,25 @@ def test_append_index(): tm.assert_index_equal(result, expected) +@pytest.mark.parametrize("name, exp", [("b", "b"), ("c", None)]) +def test_append_names_match(name, exp): + # GH#48288 + midx = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["a", "b"]) + midx2 = MultiIndex.from_arrays([[3], [5]], names=["a", name]) + result = midx.append(midx2) + expected = MultiIndex.from_arrays([[1, 2, 3], [3, 4, 5]], names=["a", exp]) + tm.assert_index_equal(result, expected) + + +def test_append_names_dont_match(): + # GH#48288 + midx = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["a", "b"]) + midx2 = MultiIndex.from_arrays([[3], [5]], names=["x", "y"]) + result = midx.append(midx2) + expected = MultiIndex.from_arrays([[1, 2, 3], [3, 4, 5]], names=None) + tm.assert_index_equal(result, expected) + + def test_repeat(): reps = 2 numbers = [1, 2, 3]