From b1108fb0932055fa5e4c87a4ff8dc296458c1965 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 29 Jan 2022 16:22:34 -0800 Subject: [PATCH 1/2] BUG: Frame.iat item_cache invalidation bug --- doc/source/whatsnew/v1.5.0.rst | 1 + pandas/core/frame.py | 14 +++++++------- pandas/tests/indexing/test_iat.py | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 1d4054d5ea0f1..39b5657c3ba26 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -268,6 +268,7 @@ Indexing - Bug in :meth:`Series.__setitem__` where setting :attr:`NA` into a numeric-dtpye :class:`Series` would incorrectly upcast to object-dtype rather than treating the value as ``np.nan`` (:issue:`44199`) - Bug in :meth:`DataFrame.mask` with ``inplace=True`` and ``ExtensionDtype`` columns incorrectly raising (:issue:`45577`) - Bug in getting a column from a DataFrame with an object-dtype row index with datetime-like values: the resulting Series now preserves the exact object-dtype Index from the parent DataFrame (:issue:`42950`) +- Bug in :meth:`DataFrame.iat` setting values leading to not propagating correctly in subsequent lookups (:issue:`45684`) - Bug in indexing on a :class:`DatetimeIndex` with a ``np.str_`` key incorrectly raising (:issue:`45580`) - Bug in :meth:`CategoricalIndex.get_indexer` when index contains ``NaN`` values, resulting in elements that are in target but not present in the index to be mapped to the index of the NaN element, instead of -1 (:issue:`45361`) - diff --git a/pandas/core/frame.py b/pandas/core/frame.py index d76af1ce42546..e72c20f7eb504 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -3878,15 +3878,15 @@ def _set_value( try: if takeable: series = self._ixs(col, axis=1) - series._set_value(index, value, takeable=True) - return - - series = self._get_item_cache(col) - loc = self.index.get_loc(index) + loc = index + else: + series = self._get_item_cache(col) + loc = self.index.get_loc(index) - # series._set_value will do validation that may raise TypeError + # setitem_inplace will do validation that may raise TypeError # or ValueError - series._set_value(loc, value, takeable=True) + series._mgr.setitem_inplace(loc, value) + except (KeyError, TypeError, ValueError): # set using a non-recursive method & reset the cache if takeable: diff --git a/pandas/tests/indexing/test_iat.py b/pandas/tests/indexing/test_iat.py index f1fe464ca0854..1b33645fd38f3 100644 --- a/pandas/tests/indexing/test_iat.py +++ b/pandas/tests/indexing/test_iat.py @@ -29,3 +29,20 @@ def test_iat_getitem_series_with_period_index(): expected = ser[index[0]] result = ser.iat[0] assert expected == result + + +def test_iat_setitem_item_cache_cleared(): + # GH#45684 + data = {"x": np.arange(8, dtype=np.int64), "y": np.int64(0)} + df = DataFrame(data).copy() + ser = df["y"] + + # previously this iat setting would split the block and fail to clear + # the item_cache. + df.iat[7, 0] = 9999 + + df.iat[7, 1] = 1234 + + assert df.iat[7, 1] == 1234 + assert ser.iloc[-1] == 1234 + assert df.iloc[-1, -1] == 1234 From 7b56aadade48b8095fc426420383aa12590c4fe5 Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 30 Jan 2022 12:28:50 -0800 Subject: [PATCH 2/2] move whatsnew, test iloc --- doc/source/whatsnew/v1.4.1.rst | 3 ++- doc/source/whatsnew/v1.5.0.rst | 1 - pandas/tests/indexing/test_iat.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v1.4.1.rst b/doc/source/whatsnew/v1.4.1.rst index 10b605f6ef43e..1604d5d1950d7 100644 --- a/doc/source/whatsnew/v1.4.1.rst +++ b/doc/source/whatsnew/v1.4.1.rst @@ -17,9 +17,10 @@ Fixed regressions - Regression in :meth:`Series.mask` with ``inplace=True`` and ``PeriodDtype`` and an incompatible ``other`` coercing to a common dtype instead of raising (:issue:`45546`) - Regression in :func:`.assert_frame_equal` not respecting ``check_flags=False`` (:issue:`45554`) - Regression in :meth:`Series.fillna` with ``downcast=False`` incorrectly downcasting ``object`` dtype (:issue:`45603`) +- Regression in :meth:`DataFrame.iat` setting values leading to not propagating correctly in subsequent lookups (:issue:`45684`) - Regression in :meth:`DataFrame.loc.__setitem__` losing :class:`Index` name if :class:`DataFrame` was empty before (:issue:`45621`) - Regression in :func:`join` with overlapping :class:`IntervalIndex` raising an ``InvalidIndexError`` (:issue:`45661`) -- + .. --------------------------------------------------------------------------- diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index c4cef25eb4d12..0f5b4a16d2f01 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -270,7 +270,6 @@ Indexing - Bug in :meth:`Series.__setitem__` where setting :attr:`NA` into a numeric-dtpye :class:`Series` would incorrectly upcast to object-dtype rather than treating the value as ``np.nan`` (:issue:`44199`) - Bug in :meth:`DataFrame.mask` with ``inplace=True`` and ``ExtensionDtype`` columns incorrectly raising (:issue:`45577`) - Bug in getting a column from a DataFrame with an object-dtype row index with datetime-like values: the resulting Series now preserves the exact object-dtype Index from the parent DataFrame (:issue:`42950`) -- Bug in :meth:`DataFrame.iat` setting values leading to not propagating correctly in subsequent lookups (:issue:`45684`) - Bug in indexing on a :class:`DatetimeIndex` with a ``np.str_`` key incorrectly raising (:issue:`45580`) - Bug in :meth:`CategoricalIndex.get_indexer` when index contains ``NaN`` values, resulting in elements that are in target but not present in the index to be mapped to the index of the NaN element, instead of -1 (:issue:`45361`) - diff --git a/pandas/tests/indexing/test_iat.py b/pandas/tests/indexing/test_iat.py index 1b33645fd38f3..44bd51ee1b7d1 100644 --- a/pandas/tests/indexing/test_iat.py +++ b/pandas/tests/indexing/test_iat.py @@ -31,7 +31,7 @@ def test_iat_getitem_series_with_period_index(): assert expected == result -def test_iat_setitem_item_cache_cleared(): +def test_iat_setitem_item_cache_cleared(indexer_ial): # GH#45684 data = {"x": np.arange(8, dtype=np.int64), "y": np.int64(0)} df = DataFrame(data).copy() @@ -39,9 +39,9 @@ def test_iat_setitem_item_cache_cleared(): # previously this iat setting would split the block and fail to clear # the item_cache. - df.iat[7, 0] = 9999 + indexer_ial(df)[7, 0] = 9999 - df.iat[7, 1] = 1234 + indexer_ial(df)[7, 1] = 1234 assert df.iat[7, 1] == 1234 assert ser.iloc[-1] == 1234