From 1fdc3849261cc34f3eeb04192409aaec0800363a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 11 Feb 2020 12:59:51 -0800 Subject: [PATCH 1/2] BIG: fix length_of_indexer with boolean mask --- pandas/core/indexers.py | 8 +++++++- pandas/core/indexing.py | 28 +++++++--------------------- pandas/tests/test_indexers.py | 11 +++++++++++ 3 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 pandas/tests/test_indexers.py diff --git a/pandas/core/indexers.py b/pandas/core/indexers.py index fe475527f4596..90a58a6308f40 100644 --- a/pandas/core/indexers.py +++ b/pandas/core/indexers.py @@ -219,7 +219,7 @@ def maybe_convert_indices(indices, n: int): def length_of_indexer(indexer, target=None) -> int: """ - Return the length of a single non-tuple indexer which could be a slice. + Return the expected length of target[indexer] Returns ------- @@ -245,6 +245,12 @@ def length_of_indexer(indexer, target=None) -> int: step = -step return (stop - start + step - 1) // step elif isinstance(indexer, (ABCSeries, ABCIndexClass, np.ndarray, list)): + if isinstance(indexer, list): + indexer = np.array(indexer) + + if indexer.dtype == bool: + # GH#25774 + return indexer.sum() return len(indexer) elif not is_list_like_indexer(indexer): return 1 diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 44b3c318366d2..b89c8812d6744 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1702,31 +1702,17 @@ def _setitem_with_indexer(self, indexer, value): plane_indexer = tuple([idx]) + indexer[info_axis + 1 :] lplane_indexer = length_of_indexer(plane_indexer[0], index) + # lplane_indexer gives the expected length of obj[idx] # require that we are setting the right number of values that # we are indexing - if ( - is_list_like_indexer(value) - and np.iterable(value) - and lplane_indexer != len(value) - ): - - if len(obj[idx]) != len(value): - raise ValueError( - "cannot set using a multi-index " - "selection indexer with a different " - "length than the value" - ) - - # make sure we have an ndarray - value = getattr(value, "values", value).ravel() + if is_list_like_indexer(value) and lplane_indexer != len(value): - # we can directly set the series here - obj._consolidate_inplace() - obj = obj.copy() - obj._data = obj._data.setitem(indexer=tuple([idx]), value=value) - self.obj[item] = obj - return + raise ValueError( + "cannot set using a multi-index " + "selection indexer with a different " + "length than the value" + ) # non-mi else: diff --git a/pandas/tests/test_indexers.py b/pandas/tests/test_indexers.py new file mode 100644 index 0000000000000..173f33b19f8d5 --- /dev/null +++ b/pandas/tests/test_indexers.py @@ -0,0 +1,11 @@ +# Tests aimed at pandas.core.indexers +import numpy as np + +from pandas.core.indexers import length_of_indexer + + +def test_length_of_indexer(): + arr = np.zeros(4, dtype=bool) + arr[0] = 1 + result = length_of_indexer(arr) + assert result == 1 From 3d43fc6560637f6071e94a0cbecdd1b4a9ee46fc Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 13 Feb 2020 09:16:08 -0800 Subject: [PATCH 2/2] rename file --- pandas/tests/{ => indexing}/test_indexers.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pandas/tests/{ => indexing}/test_indexers.py (100%) diff --git a/pandas/tests/test_indexers.py b/pandas/tests/indexing/test_indexers.py similarity index 100% rename from pandas/tests/test_indexers.py rename to pandas/tests/indexing/test_indexers.py