From 02d37ec3ce89b8c823ee3d2f7a49421a447a76e0 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 10 Feb 2020 20:42:13 -0800 Subject: [PATCH 1/8] re-order check --- pandas/core/indexing.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index c7dcccab00d95..5a4e4ee5d8cfc 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -615,14 +615,7 @@ def _get_setitem_indexer(self, key): if isinstance(key, range): return list(key) - try: - return self._convert_to_indexer(key, axis=0, is_setter=True) - except TypeError as e: - - # invalid indexer type vs 'other' indexing errors - if "cannot do" in str(e): - raise - raise IndexingError(key) + return self._convert_to_indexer(key, axis=0, is_setter=True) def __setitem__(self, key, value): if isinstance(key, tuple): @@ -1823,7 +1816,6 @@ def setter(item, v): else: if isinstance(indexer, tuple): - indexer = maybe_convert_ix(*indexer) # if we are setting on the info axis ONLY # set using those methods to avoid block-splitting @@ -1841,6 +1833,8 @@ def setter(item, v): self.obj[item_labels[indexer[info_axis]]] = value return + indexer = maybe_convert_ix(*indexer) + if isinstance(value, (ABCSeries, dict)): # TODO(EA): ExtensionBlock.setitem this causes issues with # setting for extensionarrays that store dicts. Need to decide From 2dc93768bc32bacb9fb166c46085879dfcaea693 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 11 Feb 2020 11:04:27 -0800 Subject: [PATCH 2/8] CLN: de-nest bits of Series.__setitem__ --- pandas/core/series.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 34ebbaf79e5e9..171a94878fac1 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1005,14 +1005,14 @@ def __setitem__(self, key, value): key = com.apply_if_callable(key, self) cacher_needs_updating = self._check_is_chained_assignment_possible() + if key is Ellipsis: + key = slice(None) + try: self._set_with_engine(key, value) except (KeyError, ValueError): - values = self._values if is_integer(key) and not self.index.inferred_type == "integer": - values[key] = value - elif key is Ellipsis: - self[:] = value + self._values[key] = value else: self.loc[key] = value @@ -1030,9 +1030,10 @@ def __setitem__(self, key, value): self._where(~key, value, inplace=True) return except InvalidIndexError: - pass + self._set_values(key.astype(np.bool_), value) - self._set_with(key, value) + else: + self._set_with(key, value) if cacher_needs_updating: self._maybe_update_cacher() @@ -1073,13 +1074,13 @@ def _set_with(self, key, value): else: key_type = lib.infer_dtype(key, skipna=False) + # Note: key_type == "boolean" should be handled by the + # com.is_bool_indexer block in __getitem__ if key_type == "integer": if self.index.inferred_type == "integer": self._set_labels(key, value) else: return self._set_values(key, value) - elif key_type == "boolean": - self._set_values(key.astype(np.bool_), value) else: self._set_labels(key, value) From 294a37d2db417e24dab839c428c64d22794ddf3f Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 11 Feb 2020 11:56:51 -0800 Subject: [PATCH 3/8] linearize Series.__setitem__ --- pandas/core/series.py | 51 ++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 171a94878fac1..32ddaa8ab12f5 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1008,31 +1008,35 @@ def __setitem__(self, key, value): if key is Ellipsis: key = slice(None) - try: - self._set_with_engine(key, value) - except (KeyError, ValueError): - if is_integer(key) and not self.index.inferred_type == "integer": - self._values[key] = value - else: - self.loc[key] = value + if isinstance(key, slice): + indexer = self.index._convert_slice_indexer(key, kind="getitem") + self._set_values(indexer, value) - except TypeError as e: - if isinstance(key, tuple) and not isinstance(self.index, MultiIndex): - raise ValueError("Can only tuple-index with a MultiIndex") + elif com.is_bool_indexer(key): + key = check_bool_indexer(self.index, key) + try: + self._where(~key, value, inplace=True) + return + except InvalidIndexError: + self._set_values(key.astype(np.bool_), value) + + else: + try: + self._set_with_engine(key, value) + except (KeyError, ValueError): + if is_integer(key) and not self.index.inferred_type == "integer": + self._values[key] = value + else: + self.loc[key] = value - # python 3 type errors should be raised - if _is_unorderable_exception(e): - raise IndexError(key) + except TypeError as e: + if isinstance(key, tuple) and not isinstance(self.index, MultiIndex): + raise ValueError("Can only tuple-index with a MultiIndex") - if com.is_bool_indexer(key): - key = check_bool_indexer(self.index, key) - try: - self._where(~key, value, inplace=True) - return - except InvalidIndexError: - self._set_values(key.astype(np.bool_), value) + # python 3 type errors should be raised + if _is_unorderable_exception(e): + raise IndexError(key) - else: self._set_with(key, value) if cacher_needs_updating: @@ -1046,11 +1050,8 @@ def _set_with_engine(self, key, value): def _set_with(self, key, value): # other: fancy integer or otherwise - if isinstance(key, slice): - indexer = self.index._convert_slice_indexer(key, kind="getitem") - return self._set_values(indexer, value) - elif is_scalar(key) and not is_integer(key) and key not in self.index: + if is_scalar(key) and not is_integer(key) and key not in self.index: # GH#12862 adding an new key to the Series # Note: have to exclude integers because that is ambiguously # position-based From 38c7a5ebfcebaa670a8df7750d8cf7fe8c5b87ee Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 11 Feb 2020 14:51:55 -0800 Subject: [PATCH 4/8] revert --- pandas/core/indexing.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 7a1b37bce1b26..44b3c318366d2 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -615,7 +615,14 @@ def _get_setitem_indexer(self, key): if isinstance(key, range): return list(key) - return self._convert_to_indexer(key, axis=0, is_setter=True) + try: + return self._convert_to_indexer(key, axis=0, is_setter=True) + except TypeError as e: + + # invalid indexer type vs 'other' indexing errors + if "cannot do" in str(e): + raise + raise IndexingError(key) def __setitem__(self, key, value): if isinstance(key, tuple): @@ -1816,6 +1823,7 @@ def setter(item, v): else: if isinstance(indexer, tuple): + indexer = maybe_convert_ix(*indexer) # if we are setting on the info axis ONLY # set using those methods to avoid block-splitting @@ -1833,8 +1841,6 @@ def setter(item, v): self.obj[item_labels[indexer[info_axis]]] = value return - indexer = maybe_convert_ix(*indexer) - if isinstance(value, (ABCSeries, dict)): # TODO(EA): ExtensionBlock.setitem this causes issues with # setting for extensionarrays that store dicts. Need to decide From af0a4439595012d40eb3e5a5bc4c369d19f9131d Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 18 Feb 2020 09:26:23 -0800 Subject: [PATCH 5/8] revert --- pandas/core/series.py | 62 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index ee58b0a9c35c1..15fe0bb98b536 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -977,39 +977,34 @@ def __setitem__(self, key, value): key = com.apply_if_callable(key, self) cacher_needs_updating = self._check_is_chained_assignment_possible() - if key is Ellipsis: - key = slice(None) - - if isinstance(key, slice): - indexer = self.index._convert_slice_indexer(key, kind="getitem") - self._set_values(indexer, value) - - elif com.is_bool_indexer(key): - key = check_bool_indexer(self.index, key) - try: - self._where(~key, value, inplace=True) - return - except InvalidIndexError: - self._set_values(key.astype(np.bool_), value) + try: + self._set_with_engine(key, value) + except (KeyError, ValueError): + values = self._values + if is_integer(key) and not self.index.inferred_type == "integer": + values[key] = value + elif key is Ellipsis: + self[:] = value + else: + self.loc[key] = value - else: - try: - self._set_with_engine(key, value) - except (KeyError, ValueError): - if is_integer(key) and not self.index.inferred_type == "integer": - self._values[key] = value - else: - self.loc[key] = value + except TypeError as e: + if isinstance(key, tuple) and not isinstance(self.index, MultiIndex): + raise ValueError("Can only tuple-index with a MultiIndex") - except TypeError as e: - if isinstance(key, tuple) and not isinstance(self.index, MultiIndex): - raise ValueError("Can only tuple-index with a MultiIndex") + # python 3 type errors should be raised + if _is_unorderable_exception(e): + raise IndexError(key) - # python 3 type errors should be raised - if _is_unorderable_exception(e): - raise IndexError(key) + if com.is_bool_indexer(key): + key = check_bool_indexer(self.index, key) + try: + self._where(~key, value, inplace=True) + return + except InvalidIndexError: + pass - self._set_with(key, value) + self._set_with(key, value) if cacher_needs_updating: self._maybe_update_cacher() @@ -1022,8 +1017,11 @@ def _set_with_engine(self, key, value): def _set_with(self, key, value): # other: fancy integer or otherwise + if isinstance(key, slice): + indexer = self.index._convert_slice_indexer(key, kind="getitem") + return self._set_values(indexer, value) - if is_scalar(key) and not is_integer(key) and key not in self.index: + elif is_scalar(key) and not is_integer(key) and key not in self.index: # GH#12862 adding an new key to the Series # Note: have to exclude integers because that is ambiguously # position-based @@ -1047,13 +1045,13 @@ def _set_with(self, key, value): else: key_type = lib.infer_dtype(key, skipna=False) - # Note: key_type == "boolean" should be handled by the - # com.is_bool_indexer block in __getitem__ if key_type == "integer": if self.index.inferred_type == "integer": self._set_labels(key, value) else: return self._set_values(key, value) + elif key_type == "boolean": + self._set_values(key.astype(np.bool_), value) else: self._set_labels(key, value) From e4d68d49bdda79749372a889d1ad948dea12de40 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 18 Feb 2020 09:30:13 -0800 Subject: [PATCH 6/8] CLN: simplify Series.__setitem__ --- pandas/core/series.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 15fe0bb98b536..9e54f94204ac1 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -977,14 +977,15 @@ def __setitem__(self, key, value): key = com.apply_if_callable(key, self) cacher_needs_updating = self._check_is_chained_assignment_possible() + if key is Ellipsis: + key = slice(None) + try: self._set_with_engine(key, value) except (KeyError, ValueError): values = self._values if is_integer(key) and not self.index.inferred_type == "integer": values[key] = value - elif key is Ellipsis: - self[:] = value else: self.loc[key] = value @@ -1002,9 +1003,10 @@ def __setitem__(self, key, value): self._where(~key, value, inplace=True) return except InvalidIndexError: - pass + self._set_values(key.astype(np.bool_), value) - self._set_with(key, value) + else: + self._set_with(key, value) if cacher_needs_updating: self._maybe_update_cacher() @@ -1045,13 +1047,13 @@ def _set_with(self, key, value): else: key_type = lib.infer_dtype(key, skipna=False) + # Note: key_type == "boolean" should not occur because that + # should be caught by the is_bool_indexer check in __setitem__ if key_type == "integer": if self.index.inferred_type == "integer": self._set_labels(key, value) else: return self._set_values(key, value) - elif key_type == "boolean": - self._set_values(key.astype(np.bool_), value) else: self._set_labels(key, value) From ec4a201d90aa083de49452165ac9864027010fd5 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 22 Feb 2020 08:43:28 -0800 Subject: [PATCH 7/8] add assertion --- pandas/core/series.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 479cde4bc4df8..f2965424c7d4e 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -905,8 +905,10 @@ def _get_with(self, key): else: key_type = lib.infer_dtype(key, skipna=False) - # Note: The key_type == "boolean" case should be caught by the + # The key_type == "boolean" case should be caught by the # com.is_bool_indexer check in __getitem__ + assert key_type != "boolean" + if key_type == "integer": # We need to decide whether to treat this as a positional indexer # (i.e. self.iloc) or label-based (i.e. self.loc) From f0cdc67e82e1fc9740a1d721862c18d84723e9d9 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 23 Feb 2020 09:08:15 -0800 Subject: [PATCH 8/8] restore symmetry --- pandas/core/series.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index c14fb36f9edcf..cc2752040aa82 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -905,10 +905,8 @@ def _get_with(self, key): else: key_type = lib.infer_dtype(key, skipna=False) - # The key_type == "boolean" case should be caught by the + # Note: The key_type == "boolean" case should be caught by the # com.is_bool_indexer check in __getitem__ - assert key_type != "boolean" - if key_type == "integer": # We need to decide whether to treat this as a positional indexer # (i.e. self.iloc) or label-based (i.e. self.loc)