From f810831a65b1dcb5f56b0f270ccd50ab0f0ecdeb Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Fri, 20 Mar 2020 13:40:27 -0700 Subject: [PATCH] BUG: is_scalar_indexer (#32850) * BUG: is_scalar_indexer * update docstring * add copy=False --- pandas/core/indexers.py | 18 +++++++++++++----- pandas/core/internals/blocks.py | 8 +++----- pandas/tests/indexing/test_indexers.py | 19 ++++++++++++++++++- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/pandas/core/indexers.py b/pandas/core/indexers.py index 71fd5b6aab821..3d0e3699264a8 100644 --- a/pandas/core/indexers.py +++ b/pandas/core/indexers.py @@ -65,18 +65,26 @@ def is_list_like_indexer(key) -> bool: return is_list_like(key) and not (isinstance(key, tuple) and type(key) is not tuple) -def is_scalar_indexer(indexer, arr_value) -> bool: +def is_scalar_indexer(indexer, ndim: int) -> bool: """ Return True if we are all scalar indexers. + Parameters + ---------- + indexer : object + ndim : int + Number of dimensions in the object being indexed. + Returns ------- bool """ - if arr_value.ndim == 1: - if not isinstance(indexer, tuple): - indexer = tuple([indexer]) - return any(isinstance(idx, np.ndarray) and len(idx) == 0 for idx in indexer) + if isinstance(indexer, tuple): + if len(indexer) == ndim: + return all( + is_integer(x) or (isinstance(x, np.ndarray) and x.ndim == len(x) == 1) + for x in indexer + ) return False diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index fec8639f5a44d..ce3c34d33021b 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -874,7 +874,7 @@ def setitem(self, indexer, value): # GH#8669 empty indexers pass - elif is_scalar_indexer(indexer, arr_value): + elif is_scalar_indexer(indexer, self.ndim): # setting a single element for each dim and with a rhs that could # be e.g. a list; see GH#6043 values[indexer] = value @@ -892,12 +892,10 @@ def setitem(self, indexer, value): # if we are an exact match (ex-broadcasting), # then use the resultant dtype elif exact_match: + # We are setting _all_ of the array's values, so can cast to new dtype values[indexer] = value - try: - values = values.astype(arr_value.dtype) - except ValueError: - pass + values = values.astype(arr_value.dtype, copy=False) # set else: diff --git a/pandas/tests/indexing/test_indexers.py b/pandas/tests/indexing/test_indexers.py index 173f33b19f8d5..35c0c06e86099 100644 --- a/pandas/tests/indexing/test_indexers.py +++ b/pandas/tests/indexing/test_indexers.py @@ -1,7 +1,7 @@ # Tests aimed at pandas.core.indexers import numpy as np -from pandas.core.indexers import length_of_indexer +from pandas.core.indexers import is_scalar_indexer, length_of_indexer def test_length_of_indexer(): @@ -9,3 +9,20 @@ def test_length_of_indexer(): arr[0] = 1 result = length_of_indexer(arr) assert result == 1 + + +def test_is_scalar_indexer(): + indexer = (0, 1) + assert is_scalar_indexer(indexer, 2) + assert not is_scalar_indexer(indexer[0], 2) + + indexer = (np.array([2]), 1) + assert is_scalar_indexer(indexer, 2) + + indexer = (np.array([2]), np.array([3])) + assert is_scalar_indexer(indexer, 2) + + indexer = (np.array([2]), np.array([3, 4])) + assert not is_scalar_indexer(indexer, 2) + + assert not is_scalar_indexer(slice(None), 1)