Repository: pandas-dev/pandas (Python)

Issue:
BUG: Setting values with ILOC and list like indexers raises 
- [x] I have checked that this issue has not already been reported.

- [x] I have confirmed this bug exists on the latest version of pandas.

- [x] (optional) I have confirmed this bug exists on the master branch of pandas.

---

**Note**: Please read [this guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) detailing how to provide the necessary information for us to reproduce your bug.

#### Code Sample, a copy-pastable example

```python
import pandas as pd


df = pd.DataFrame({"flag": ["x", "y"], "value": [1, 2]})
df.iloc[[True, False], 1] = df.iloc[[True, False], 1] * 2
print(df)

```

#### Problem description

This worked on 1.0.5 and returned 
```
  flag  value
0    x      2
1    y      2
```

On master this raises:

```
Traceback (most recent call last):
  File "/home/developer/.config/JetBrains/PyCharm2020.2/scratches/scratch_5.py", line 221, in <module>
    df.iloc[[True, False], 1] = x
  File "/home/developer/PycharmProjects/pandas/pandas/core/indexing.py", line 681, in __setitem__
    iloc._setitem_with_indexer(indexer, value)
  File "/home/developer/PycharmProjects/pandas/pandas/core/indexing.py", line 1756, in _setitem_with_indexer
    self._setitem_single_column(ilocs[0], value, pi)
  File "/home/developer/PycharmProjects/pandas/pandas/core/indexing.py", line 1800, in _setitem_single_column
    ser._mgr = ser._mgr.setitem(indexer=pi, value=value)
  File "/home/developer/PycharmProjects/pandas/pandas/core/internals/managers.py", line 532, in setitem
    return self.apply("setitem", indexer=indexer, value=value)
  File "/home/developer/PycharmProjects/pandas/pandas/core/internals/managers.py", line 397, in apply
    applied = getattr(b, f)(**kwargs)
  File "/home/developer/PycharmProjects/pandas/pandas/core/internals/blocks.py", line 923, in setitem
    check_setitem_lengths(indexer, value, values)
  File "/home/developer/PycharmProjects/pandas/pandas/core/indexers.py", line 158, in check_setitem_lengths
    raise ValueError(
ValueError: cannot set using a list-like indexer with a different length than the value

Process finished with exit code 1

```

Was this change of behavior intended?

#### Expected Output

I would expect, that this would work and returns the results from 1.0.5

#### Output of ``pd.show_versions()``

<details>

master

</details>



Discussion:
Thanks @phofl 

3da053c7d1b1786689a81b143a7afb57eb4762ad is the first bad commit
commit 3da053c7d1b1786689a81b143a7afb57eb4762ad
Author: jbrockmendel <jbrockmendel@gmail.com>
Date:   Mon Feb 17 16:15:45 2020 -0800

    BUG: fix length_of_indexer with boolean mask (#31897)

 pandas/core/indexers.py                |  8 +++++++-
 pandas/core/indexing.py                | 28 +++++++---------------------
 pandas/tests/indexing/test_indexers.py | 11 +++++++++++
 3 files changed, 25 insertions(+), 22 deletions(-)
 create mode 100644 pandas/tests/indexing/test_indexers.py

https://github.com/pandas-dev/pandas/pull/31897
cc @jbrockmendel 
Not 100% sure, but it looks like before we get to that point in Block.setitem we want to have `value = 2` instead if `value = np.array([2])`.  It'll take some more digging to see exactly where that unpacking should take place.
cc @jbrockmendel 

I looked a bit into this and found, that
https://github.com/pandas-dev/pandas/blob/18b4864b7d8638c3101ec11fd6562d2fbfe872a8/pandas/core/indexing.py#L1673
casts the ``plane_indexer`` to a list, when only one element from the indexer is True. This seems to be the reason why

```
df = pd.DataFrame({"flag": ["x", "y", "z"], "value": [1, 3, 4]})
df.iloc[[True, False, True], 1] = df.iloc[[True, False, True], 1] * 2
```

works, while 

```
df = pd.DataFrame({"flag": ["x", "y", "z"], "value": [1, 3, 4]})
df.iloc[[True, False, False], 1] = df.iloc[[True, False, False], 1] * 2
```

does not. The check raising the error is only ran, when ``pi`` is a list. This line does not seem to be necessary. I ran all tests in ``indexing`` and ``indexes`` without failure after deleting it. Do you remember why you introduced it in #31837?



Patch:
diff --git a/doc/source/whatsnew/v1.1.4.rst b/doc/source/whatsnew/v1.1.4.rst
index 33a52353bed7e..332be365e3763 100644
--- a/doc/source/whatsnew/v1.1.4.rst
+++ b/doc/source/whatsnew/v1.1.4.rst
@@ -26,6 +26,7 @@ Fixed regressions
 - Fixed regression where slicing :class:`DatetimeIndex` raised :exc:`AssertionError` on irregular time series with ``pd.NaT`` or on unsorted indices (:issue:`36953` and :issue:`35509`)
 - Fixed regression in certain offsets (:meth:`pd.offsets.Day() <pandas.tseries.offsets.Day>` and below) no longer being hashable (:issue:`37267`)
 - Fixed regression in :class:`StataReader` which required ``chunksize`` to be manually set when using an iterator to read a dataset (:issue:`37280`)
+- Fixed regression in setitem with :meth:`DataFrame.iloc` which raised error when trying to set a value while filtering with a boolean list (:issue:`36741`)
 - Fixed regression in :attr:`MultiIndex.is_monotonic_increasing` returning wrong results with ``NaN`` in at least one of the levels (:issue:`37220`)
 
 .. ---------------------------------------------------------------------------
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
index 27ee91fc5465e..3d491c7127e38 100644
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -1670,8 +1670,6 @@ def _setitem_with_indexer(self, indexer, value):
                         "length than the value"
                     )
 
-            pi = plane_indexer[0] if lplane_indexer == 1 else plane_indexer
-
             # we need an iterable, with a ndim of at least 1
             # eg. don't pass through np.array(0)
             if is_list_like_indexer(value) and getattr(value, "ndim", 1) > 0:
@@ -1698,7 +1696,7 @@ def _setitem_with_indexer(self, indexer, value):
                             else:
                                 v = np.nan
 
-                            self._setitem_single_column(loc, v, pi)
+                            self._setitem_single_column(loc, v, plane_indexer)
 
                     elif not unique_cols:
                         raise ValueError(
@@ -1716,7 +1714,7 @@ def _setitem_with_indexer(self, indexer, value):
                             else:
                                 v = np.nan
 
-                            self._setitem_single_column(loc, v, pi)
+                            self._setitem_single_column(loc, v, plane_indexer)
 
                 # we have an equal len ndarray/convertible to our labels
                 # hasattr first, to avoid coercing to ndarray without reason.
@@ -1735,7 +1733,9 @@ def _setitem_with_indexer(self, indexer, value):
 
                     for i, loc in enumerate(ilocs):
                         # setting with a list, re-coerces
-                        self._setitem_single_column(loc, value[:, i].tolist(), pi)
+                        self._setitem_single_column(
+                            loc, value[:, i].tolist(), plane_indexer
+                        )
 
                 elif (
                     len(labels) == 1
@@ -1744,7 +1744,7 @@ def _setitem_with_indexer(self, indexer, value):
                 ):
                     # we have an equal len list/ndarray
                     # We only get here with len(labels) == len(ilocs) == 1
-                    self._setitem_single_column(ilocs[0], value, pi)
+                    self._setitem_single_column(ilocs[0], value, plane_indexer)
 
                 elif lplane_indexer == 0 and len(value) == len(self.obj.index):
                     # We get here in one case via .loc with a all-False mask
@@ -1759,12 +1759,12 @@ def _setitem_with_indexer(self, indexer, value):
                         )
 
                     for loc, v in zip(ilocs, value):
-                        self._setitem_single_column(loc, v, pi)
+                        self._setitem_single_column(loc, v, plane_indexer)
             else:
 
                 # scalar value
                 for loc in ilocs:
-                    self._setitem_single_column(loc, value, pi)
+                    self._setitem_single_column(loc, value, plane_indexer)
 
         else:
             self._setitem_single_block(indexer, value)


Test patch:
diff --git a/pandas/tests/frame/indexing/test_setitem.py b/pandas/tests/frame/indexing/test_setitem.py
index c317e90181a8f..55465dffd2027 100644
--- a/pandas/tests/frame/indexing/test_setitem.py
+++ b/pandas/tests/frame/indexing/test_setitem.py
@@ -229,3 +229,12 @@ def test_setitem_periodindex(self):
         rs = df.reset_index().set_index("index")
         assert isinstance(rs.index, PeriodIndex)
         tm.assert_index_equal(rs.index, rng)
+
+    @pytest.mark.parametrize("klass", [list, np.array])
+    def test_iloc_setitem_bool_indexer(self, klass):
+        # GH: 36741
+        df = DataFrame({"flag": ["x", "y", "z"], "value": [1, 3, 4]})
+        indexer = klass([True, False, False])
+        df.iloc[indexer, 1] = df.iloc[indexer, 1] * 2
+        expected = DataFrame({"flag": ["x", "y", "z"], "value": [2, 3, 4]})
+        tm.assert_frame_equal(df, expected)