Skip to content

Commit f6a4f29

Browse files
authored
Regression in loc.setitem raising ValueError with unordered MultiIndex columns and scalar indexer (#39071)
* Regression in loc.setitem raising ValueError with unordered MultiIndex columns and scalar indexer * Add tests * Improve performance
1 parent e8efc62 commit f6a4f29

File tree

3 files changed

+22
-3
lines changed

3 files changed

+22
-3
lines changed

doc/source/whatsnew/v1.2.1.rst

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Fixed regressions
2222
- Fixed regression in :meth:`DataFrame.any` and :meth:`DataFrame.all` not returning a result for tz-aware ``datetime64`` columns (:issue:`38723`)
2323
- Fixed regression in :meth:`DataFrame.__setitem__` raising ``ValueError`` when expanding :class:`DataFrame` and new column is from type ``"0 - name"`` (:issue:`39010`)
2424
- Fixed regression in :meth:`.GroupBy.sem` where the presence of non-numeric columns would cause an error instead of being dropped (:issue:`38774`)
25+
- Fixed regression in :meth:`DataFrame.loc.__setitem__` raising ``ValueError`` when :class:`DataFrame` has unsorted :class:`MultiIndex` columns and indexer is a scalar (:issue:`38601`)
2526
- Fixed regression in :func:`read_excel` with non-rawbyte file handles (:issue:`38788`)
2627
- Bug in :meth:`read_csv` with ``float_precision="high"`` caused segfault or wrong parsing of long exponent strings. This resulted in a regression in some cases as the default for ``float_precision`` was changed in pandas 1.2.0 (:issue:`38753`)
2728
- Fixed regression in :meth:`Rolling.skew` and :meth:`Rolling.kurt` modifying the object inplace (:issue:`38908`)

pandas/core/indexing.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from pandas.core.dtypes.common import (
1717
is_array_like,
18+
is_bool_dtype,
1819
is_hashable,
1920
is_integer,
2021
is_iterator,
@@ -1933,12 +1934,14 @@ def _ensure_iterable_column_indexer(self, column_indexer):
19331934
"""
19341935
Ensure that our column indexer is something that can be iterated over.
19351936
"""
1936-
# Ensure we have something we can iterate over
19371937
if is_integer(column_indexer):
19381938
ilocs = [column_indexer]
19391939
elif isinstance(column_indexer, slice):
1940-
ri = Index(range(len(self.obj.columns)))
1941-
ilocs = ri[column_indexer]
1940+
ilocs = np.arange(len(self.obj.columns))[column_indexer]
1941+
elif isinstance(column_indexer, np.ndarray) and is_bool_dtype(
1942+
column_indexer.dtype
1943+
):
1944+
ilocs = np.arange(len(column_indexer))[column_indexer]
19421945
else:
19431946
ilocs = column_indexer
19441947
return ilocs

pandas/tests/frame/indexing/test_indexing.py

+15
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,21 @@ def test_getitem_interval_index_partial_indexing(self):
16851685
res = df.loc[:, 0.5]
16861686
tm.assert_series_equal(res, expected)
16871687

1688+
@pytest.mark.parametrize("indexer", ["A", ["A"], ("A", slice(None))])
1689+
def test_setitem_unsorted_multiindex_columns(self, indexer):
1690+
# GH#38601
1691+
mi = MultiIndex.from_tuples([("A", 4), ("B", "3"), ("A", "2")])
1692+
df = DataFrame([[1, 2, 3], [4, 5, 6]], columns=mi)
1693+
obj = df.copy()
1694+
obj.loc[:, indexer] = np.zeros((2, 2), dtype=int)
1695+
expected = DataFrame([[0, 2, 0], [0, 5, 0]], columns=mi)
1696+
tm.assert_frame_equal(obj, expected)
1697+
1698+
df = df.sort_index(1)
1699+
df.loc[:, indexer] = np.zeros((2, 2), dtype=int)
1700+
expected = expected.sort_index(1)
1701+
tm.assert_frame_equal(df, expected)
1702+
16881703

16891704
class TestDataFrameIndexingUInt64:
16901705
def test_setitem(self, uint64_frame):

0 commit comments

Comments
 (0)