Skip to content

Commit 88fecc0

Browse files
committed
BUG: Fix metadata mutation in MultiIndex set operations
1 parent f8d9770 commit 88fecc0

File tree

3 files changed

+28
-5
lines changed

3 files changed

+28
-5
lines changed

pandas/core/indexes/multi.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4082,13 +4082,12 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
40824082
def _get_reconciled_name_object(self, other) -> MultiIndex:
40834083
"""
40844084
If the result of a set operation will be self,
4085-
return self, unless the names change, in which
4086-
case make a shallow copy of self.
4085+
return a shallow copy of self.
40874086
"""
40884087
names = self._maybe_match_names(other)
40894088
if self.names != names:
40904089
return self.rename(names)
4091-
return self
4090+
return self.copy()
40924091

40934092
def _maybe_match_names(self, other):
40944093
"""

pandas/tests/indexes/test_setops.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,3 +1039,27 @@ def test_union_mutation_safety_other(self):
10391039

10401040
assert result.name == "original"
10411041
assert index2.name == "changed"
1042+
1043+
def test_multiindex_intersection_mutation_safety(self):
1044+
# GH#63169
1045+
mi1 = MultiIndex.from_tuples([("a", 1), ("b", 2)], names=["x", "y"])
1046+
mi2 = MultiIndex.from_tuples([("a", 1), ("b", 2)], names=["x", "y"])
1047+
1048+
result = mi1.intersection(mi2)
1049+
assert result is not mi1
1050+
1051+
mi1.names = ["changed1", "changed2"]
1052+
assert result.names == ["x", "y"]
1053+
1054+
def test_multiindex_union_mutation_safety(self):
1055+
# GH#63169
1056+
mi1 = MultiIndex.from_tuples([("a", 1), ("b", 2)], names=["x", "y"])
1057+
mi2 = MultiIndex.from_tuples([("a", 1), ("b", 2)], names=["x", "y"])
1058+
1059+
result = mi1.union(mi2)
1060+
assert result is not mi1
1061+
1062+
mi1.names = ["changed1", "changed2"]
1063+
assert result.names == ["x", "y"]
1064+
1065+

pandas/tests/indexes/timedeltas/test_setops.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def test_intersection_bug_1708(self):
114114

115115
def test_intersection_equal(self, sort):
116116
# GH 24471 Test intersection outcome given the sort keyword
117-
# for equal indices intersection should return the original index
117+
# GH#63169 intersection returns a copy to prevent shared mutable state
118118
first = timedelta_range("1 day", periods=4, freq="h")
119119
second = timedelta_range("1 day", periods=4, freq="h")
120120
intersect = first.intersection(second, sort=sort)
@@ -124,7 +124,7 @@ def test_intersection_equal(self, sort):
124124

125125
# Corner cases
126126
inter = first.intersection(first, sort=sort)
127-
assert inter is first
127+
assert inter is not first
128128

129129
@pytest.mark.parametrize("period_1, period_2", [(0, 4), (4, 0)])
130130
def test_intersection_zero_length(self, period_1, period_2, sort):

0 commit comments

Comments
 (0)