Skip to content

Commit 1888df3

Browse files
authored
CoW: Boolean indexer in MultiIndex raising read-only error (#56635)
* CoW: Boolean indexer in MultiIndex raising read-only error * Fixup * Add whatsnew
1 parent 4f5ea69 commit 1888df3

File tree

3 files changed

+24
-0
lines changed

3 files changed

+24
-0
lines changed

doc/source/whatsnew/v2.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,7 @@ Interval
761761

762762
Indexing
763763
^^^^^^^^
764+
- Bug in :meth:`DataFrame.loc` mutating a boolean indexer when :class:`DataFrame` has a :class:`MultiIndex` (:issue:`56635`)
764765
- Bug in :meth:`DataFrame.loc` when setting :class:`Series` with extension dtype into NumPy dtype (:issue:`55604`)
765766
- Bug in :meth:`Index.difference` not returning a unique set of values when ``other`` is empty or ``other`` is considered non-comparable (:issue:`55113`)
766767
- Bug in setting :class:`Categorical` values into a :class:`DataFrame` with numpy dtypes raising ``RecursionError`` (:issue:`52927`)

pandas/core/indexes/multi.py

+2
Original file line numberDiff line numberDiff line change
@@ -3488,6 +3488,8 @@ def _to_bool_indexer(indexer) -> npt.NDArray[np.bool_]:
34883488
"is not the same length as the index"
34893489
)
34903490
lvl_indexer = np.asarray(k)
3491+
if indexer is None:
3492+
lvl_indexer = lvl_indexer.copy()
34913493

34923494
elif is_list_like(k):
34933495
# a collection of labels to include from this level (these are or'd)

pandas/tests/copy_view/test_indexing.py

+21
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,27 @@ def test_series_midx_tuples_slice(using_copy_on_write, warn_copy_on_write):
11801180
tm.assert_series_equal(ser, expected)
11811181

11821182

1183+
def test_midx_read_only_bool_indexer():
1184+
# GH#56635
1185+
def mklbl(prefix, n):
1186+
return [f"{prefix}{i}" for i in range(n)]
1187+
1188+
idx = pd.MultiIndex.from_product(
1189+
[mklbl("A", 4), mklbl("B", 2), mklbl("C", 4), mklbl("D", 2)]
1190+
)
1191+
cols = pd.MultiIndex.from_tuples(
1192+
[("a", "foo"), ("a", "bar"), ("b", "foo"), ("b", "bah")], names=["lvl0", "lvl1"]
1193+
)
1194+
df = DataFrame(1, index=idx, columns=cols).sort_index().sort_index(axis=1)
1195+
1196+
mask = df[("a", "foo")] == 1
1197+
expected_mask = mask.copy()
1198+
result = df.loc[pd.IndexSlice[mask, :, ["C1", "C3"]], :]
1199+
expected = df.loc[pd.IndexSlice[:, :, ["C1", "C3"]], :]
1200+
tm.assert_frame_equal(result, expected)
1201+
tm.assert_series_equal(mask, expected_mask)
1202+
1203+
11831204
def test_loc_enlarging_with_dataframe(using_copy_on_write):
11841205
df = DataFrame({"a": [1, 2, 3]})
11851206
rhs = DataFrame({"b": [1, 2, 3], "c": [4, 5, 6]})

0 commit comments

Comments
 (0)