Skip to content

Commit c80f656

Browse files
BUG: frame[x].loc[y] inconsistent with frame.at[x, y] GH#22372 (#45287)
Co-authored-by: Jeff Reback <[email protected]>
1 parent 37c43da commit c80f656

File tree

4 files changed

+32
-4
lines changed

4 files changed

+32
-4
lines changed

doc/source/whatsnew/v1.5.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ Interval
200200

201201
Indexing
202202
^^^^^^^^
203+
- Bug in :meth:`loc.__getitem__` with a list of keys causing an internal inconsistency that could lead to a disconnect between ``frame.at[x, y]`` vs ``frame[y].loc[x]`` (:issue:`22372`)
203204
- Bug in :meth:`DataFrame.iloc` where indexing a single row on a :class:`DataFrame` with a single ExtensionDtype column gave a copy instead of a view on the underlying data (:issue:`45241`)
204205
- Bug in :meth:`Series.__setitem__` with a non-integer :class:`Index` when using an integer key to set a value that cannot be set inplace where a ``ValueError`` was raised insead of casting to a common dtype (:issue:`45070`)
205206
- Bug when setting an integer too large for a :class:`Series` dtype failing to coerce to a common type (:issue:`26049`)

pandas/core/internals/managers.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -671,9 +671,6 @@ def reindex_indexer(
671671
result.axes[axis] = new_axis
672672
return result
673673

674-
if consolidate:
675-
self._consolidate_inplace()
676-
677674
# some axes don't allow reindexing with dups
678675
if not allow_dups:
679676
self.axes[axis]._validate_can_reindex(indexer)
@@ -1681,6 +1678,10 @@ def _consolidate_check(self) -> None:
16811678
self._known_consolidated = True
16821679

16831680
def _consolidate_inplace(self) -> None:
1681+
# In general, _consolidate_inplace should only be called via
1682+
# DataFrame._consolidate_inplace, otherwise we will fail to invalidate
1683+
# the DataFrame's _item_cache. The exception is for newly-created
1684+
# BlockManager objects not yet attached to a DataFrame.
16841685
if not self.is_consolidated():
16851686
self.blocks = tuple(_consolidate(self.blocks))
16861687
self._is_consolidated = True

pandas/tests/indexing/test_at.py

+23
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,29 @@ def test_selection_methods_of_assigned_col():
4848

4949

5050
class TestAtSetItem:
51+
def test_at_setitem_item_cache_cleared(self):
52+
# GH#22372 Note the multi-step construction is necessary to trigger
53+
# the original bug. pandas/issues/22372#issuecomment-413345309
54+
df = DataFrame(index=[0])
55+
df["x"] = 1
56+
df["cost"] = 2
57+
58+
# accessing df["cost"] adds "cost" to the _item_cache
59+
df["cost"]
60+
61+
# This loc[[0]] lookup used to call _consolidate_inplace at the
62+
# BlockManager level, which failed to clear the _item_cache
63+
df.loc[[0]]
64+
65+
df.at[0, "x"] = 4
66+
df.at[0, "cost"] = 789
67+
68+
expected = DataFrame({"x": [4], "cost": 789}, index=[0])
69+
tm.assert_frame_equal(df, expected)
70+
71+
# And in particular, check that the _item_cache has updated correctly.
72+
tm.assert_series_equal(df["cost"], expected["cost"])
73+
5174
def test_at_setitem_mixed_index_assignment(self):
5275
# GH#19860
5376
ser = Series([1, 2, 3, 4, 5], index=["a", "b", "c", 1, 2])

pandas/tests/internals/test_internals.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,10 @@ def test_reindex_items(self):
722722
mgr = create_mgr("a: f8; b: i8; c: f8; d: i8; e: f8; f: bool; g: f8-2")
723723

724724
reindexed = mgr.reindex_axis(["g", "c", "a", "d"], axis=0)
725-
assert reindexed.nblocks == 2
725+
# reindex_axis does not consolidate_inplace, as that risks failing to
726+
# invalidate _item_cache
727+
assert not reindexed.is_consolidated()
728+
726729
tm.assert_index_equal(reindexed.items, Index(["g", "c", "a", "d"]))
727730
tm.assert_almost_equal(
728731
mgr.iget(6).internal_values(), reindexed.iget(0).internal_values()

0 commit comments

Comments
 (0)