Skip to content

Commit bc34e24

Browse files
authored
BUG: Fix incosistent behavior of 2d-indexed loc-set to Series #59933 (#60052)
1 parent a66f59c commit bc34e24

File tree

3 files changed

+73
-3
lines changed

3 files changed

+73
-3
lines changed

doc/source/whatsnew/v3.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ Indexing
692692
^^^^^^^^
693693
- Bug in :meth:`DataFrame.__getitem__` returning modified columns when called with ``slice`` in Python 3.12 (:issue:`57500`)
694694
- Bug in :meth:`DataFrame.from_records` throwing a ``ValueError`` when passed an empty list in ``index`` (:issue:`58594`)
695+
- Bug in :meth:`DataFrame.loc` with inconsistent behavior of loc-set with 2 given indexes to Series (:issue:`59933`)
695696
- Bug in :meth:`MultiIndex.insert` when a new value inserted to a datetime-like level gets cast to ``NaT`` and fails indexing (:issue:`60388`)
696697
- Bug in printing :attr:`Index.names` and :attr:`MultiIndex.levels` would not escape single quotes (:issue:`60190`)
697698

pandas/core/indexing.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -2349,11 +2349,17 @@ def _align_series(
23492349

23502350
if isinstance(indexer, tuple):
23512351
# flatten np.ndarray indexers
2352+
if (
2353+
len(indexer) == 2
2354+
and isinstance(indexer[1], np.ndarray)
2355+
and indexer[1].dtype == np.bool_
2356+
):
2357+
indexer = (indexer[0], np.where(indexer[1])[0])
2358+
23522359
def ravel(i):
23532360
return i.ravel() if isinstance(i, np.ndarray) else i
23542361

23552362
indexer = tuple(map(ravel, indexer))
2356-
23572363
aligners = [not com.is_null_slice(idx) for idx in indexer]
23582364
sum_aligners = sum(aligners)
23592365
single_aligner = sum_aligners == 1
@@ -2371,12 +2377,15 @@ def ravel(i):
23712377

23722378
# we have a frame, with multiple indexers on both axes; and a
23732379
# series, so need to broadcast (see GH5206)
2374-
if sum_aligners == self.ndim and all(is_sequence(_) for _ in indexer):
2380+
if all(is_sequence(_) or isinstance(_, slice) for _ in indexer):
23752381
ser_values = ser.reindex(obj.axes[0][indexer[0]])._values
23762382

23772383
# single indexer
23782384
if len(indexer) > 1 and not multiindex_indexer:
2379-
len_indexer = len(indexer[1])
2385+
if isinstance(indexer[1], slice):
2386+
len_indexer = len(obj.axes[1][indexer[1]])
2387+
else:
2388+
len_indexer = len(indexer[1])
23802389
ser_values = (
23812390
np.tile(ser_values, len_indexer).reshape(len_indexer, -1).T
23822391
)

pandas/tests/indexing/test_loc.py

+60
Original file line numberDiff line numberDiff line change
@@ -3296,6 +3296,66 @@ def test_loc_reindexing_of_empty_index(self):
32963296
expected = DataFrame(index=[1, 1, 2, 2], data=["1", "1", "2", "2"])
32973297
tm.assert_frame_equal(df, expected)
32983298

3299+
@pytest.mark.parametrize(
3300+
"df, row_index, col_index, expected_df",
3301+
[
3302+
[
3303+
DataFrame([[1, 2, 3], [4, 5, 6]], columns=list("ABC")),
3304+
slice(0, 3),
3305+
["A", "B", "C"],
3306+
DataFrame([[10, 10, 10], [20, 20, 20]], columns=list("ABC")),
3307+
],
3308+
[
3309+
DataFrame([[1, 2, 3], [4, 5, 6]], columns=list("ABC")),
3310+
slice(None),
3311+
["A", "B", "C"],
3312+
DataFrame([[10, 10, 10], [20, 20, 20]], columns=list("ABC")),
3313+
],
3314+
[
3315+
DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=list("ABC")),
3316+
[True, True, True],
3317+
["A", "B", "C"],
3318+
DataFrame(
3319+
[[10, 10, 10], [20, 20, 20], [30, 30, 30]], columns=list("ABC")
3320+
),
3321+
],
3322+
[
3323+
DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=list("ABC")),
3324+
slice(0, 4),
3325+
["A", "B", "C"],
3326+
DataFrame(
3327+
[[10, 10, 10], [20, 20, 20], [30, 30, 30]], columns=list("ABC")
3328+
),
3329+
],
3330+
[
3331+
DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=list("ABC")),
3332+
slice(None),
3333+
slice("A", "C"),
3334+
DataFrame(
3335+
[[10, 10, 10], [20, 20, 20], [30, 30, 30]], columns=list("ABC")
3336+
),
3337+
],
3338+
[
3339+
DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=list("ABC")),
3340+
slice(None),
3341+
Series(
3342+
{
3343+
"A": True,
3344+
"C": False,
3345+
"B": True,
3346+
}
3347+
),
3348+
DataFrame([[10, 10, 3], [20, 20, 6], [30, 30, 9]], columns=list("ABC")),
3349+
],
3350+
],
3351+
)
3352+
def test_loc_set_series_to_multiple_columns(
3353+
self, df, row_index, col_index, expected_df
3354+
):
3355+
# GH 59933
3356+
df.loc[row_index, col_index] = Series([10, 20, 30])
3357+
tm.assert_frame_equal(df, expected_df)
3358+
32993359
def test_loc_setitem_matching_index(self):
33003360
# GH 25548
33013361
s = Series(0.0, index=list("abcd"))

0 commit comments

Comments
 (0)