|
54 | 54 | is_empty_indexer,
|
55 | 55 | is_exact_shape_match,
|
56 | 56 | is_list_like_indexer,
|
| 57 | + is_scalar_indexer, |
57 | 58 | length_of_indexer,
|
58 | 59 | )
|
59 | 60 | from pandas.core.indexes.api import (
|
@@ -671,6 +672,71 @@ def _get_setitem_indexer(self, key):
|
671 | 672 |
|
672 | 673 | return self._convert_to_indexer(key, axis=0)
|
673 | 674 |
|
| 675 | + @final |
| 676 | + def _maybe_mask_setitem_value(self, indexer, value): |
| 677 | + """ |
| 678 | + If we have obj.iloc[mask] = series_or_frame and series_or_frame has the |
| 679 | + same length as obj, we treat this as obj.iloc[mask] = series_or_frame[mask], |
| 680 | + similar to Series.__setitem__. |
| 681 | +
|
| 682 | + Note this is only for loc, not iloc. |
| 683 | + """ |
| 684 | + |
| 685 | + if ( |
| 686 | + isinstance(indexer, tuple) |
| 687 | + and len(indexer) == 2 |
| 688 | + and isinstance(value, (ABCSeries, ABCDataFrame)) |
| 689 | + ): |
| 690 | + pi, icols = indexer |
| 691 | + ndim = value.ndim |
| 692 | + if com.is_bool_indexer(pi) and len(value) == len(pi): |
| 693 | + newkey = pi.nonzero()[0] |
| 694 | + |
| 695 | + if is_scalar_indexer(icols, self.ndim - 1) and ndim == 1: |
| 696 | + # e.g. test_loc_setitem_boolean_mask_allfalse |
| 697 | + if len(newkey) == 0: |
| 698 | + # FIXME: kludge for test_loc_setitem_boolean_mask_allfalse |
| 699 | + # TODO(GH#45333): may be fixed when deprecation is enforced |
| 700 | + |
| 701 | + value = value.iloc[:0] |
| 702 | + else: |
| 703 | + # test_loc_setitem_ndframe_values_alignment |
| 704 | + value = self.obj.iloc._align_series(indexer, value) |
| 705 | + indexer = (newkey, icols) |
| 706 | + |
| 707 | + elif ( |
| 708 | + isinstance(icols, np.ndarray) |
| 709 | + and icols.dtype.kind == "i" |
| 710 | + and len(icols) == 1 |
| 711 | + ): |
| 712 | + if ndim == 1: |
| 713 | + # We implicitly broadcast, though numpy does not, see |
| 714 | + # github.com/pandas-dev/pandas/pull/45501#discussion_r789071825 |
| 715 | + if len(newkey) == 0: |
| 716 | + # FIXME: kludge for |
| 717 | + # test_setitem_loc_only_false_indexer_dtype_changed |
| 718 | + # TODO(GH#45333): may be fixed when deprecation is enforced |
| 719 | + value = value.iloc[:0] |
| 720 | + else: |
| 721 | + # test_loc_setitem_ndframe_values_alignment |
| 722 | + value = self.obj.iloc._align_series(indexer, value) |
| 723 | + indexer = (newkey, icols) |
| 724 | + |
| 725 | + elif ndim == 2 and value.shape[1] == 1: |
| 726 | + if len(newkey) == 0: |
| 727 | + # FIXME: kludge for |
| 728 | + # test_loc_setitem_all_false_boolean_two_blocks |
| 729 | + # TODO(GH#45333): may be fixed when deprecation is enforced |
| 730 | + value = value.iloc[:0] |
| 731 | + else: |
| 732 | + # test_loc_setitem_ndframe_values_alignment |
| 733 | + value = self.obj.iloc._align_frame(indexer, value) |
| 734 | + indexer = (newkey, icols) |
| 735 | + elif com.is_bool_indexer(indexer): |
| 736 | + indexer = indexer.nonzero()[0] |
| 737 | + |
| 738 | + return indexer, value |
| 739 | + |
674 | 740 | @final
|
675 | 741 | def _tupleize_axis_indexer(self, key) -> tuple:
|
676 | 742 | """
|
@@ -1309,8 +1375,7 @@ def _convert_to_indexer(self, key, axis: int):
|
1309 | 1375 |
|
1310 | 1376 | if com.is_bool_indexer(key):
|
1311 | 1377 | key = check_bool_indexer(labels, key)
|
1312 |
| - (inds,) = key.nonzero() |
1313 |
| - return inds |
| 1378 | + return key |
1314 | 1379 | else:
|
1315 | 1380 | return self._get_listlike_indexer(key, axis)[1]
|
1316 | 1381 | else:
|
@@ -1704,6 +1769,10 @@ def _setitem_with_indexer(self, indexer, value, name="iloc"):
|
1704 | 1769 | self._setitem_with_indexer_missing(indexer, value)
|
1705 | 1770 | return
|
1706 | 1771 |
|
| 1772 | + if name == "loc": |
| 1773 | + # must come after setting of missing |
| 1774 | + indexer, value = self._maybe_mask_setitem_value(indexer, value) |
| 1775 | + |
1707 | 1776 | # align and set the values
|
1708 | 1777 | if take_split_path:
|
1709 | 1778 | # We have to operate column-wise
|
|
0 commit comments