Skip to content

Commit a9a3a61

Browse files
jorisvandenbosschemeeseeksmachine
authored andcommitted
Backport PR pandas-dev#49772: PERF: first try inplace setitem for .at indexer
1 parent ec65a20 commit a9a3a61

File tree

5 files changed

+29
-10
lines changed

5 files changed

+29
-10
lines changed

asv_bench/benchmarks/indexing.py

+6
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ def setup(self):
143143
def time_loc(self):
144144
self.df.loc[self.idx_scalar, self.col_scalar]
145145

146+
def time_at(self):
147+
self.df.at[self.idx_scalar, self.col_scalar]
148+
149+
def time_at_setitem(self):
150+
self.df.at[self.idx_scalar, self.col_scalar] = 0.0
151+
146152
def time_getitem_scalar(self):
147153
self.df[self.col_scalar][self.idx_scalar]
148154

doc/source/whatsnew/v1.5.3.rst

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Fixed regressions
1818
- Fixed regression in :meth:`DataFrameGroupBy.transform` when used with ``as_index=False`` (:issue:`49834`)
1919
- Enforced reversion of ``color`` as an alias for ``c`` and ``size`` as an alias for ``s`` in function :meth:`DataFrame.plot.scatter` (:issue:`49732`)
2020
- Fixed regression in :meth:`SeriesGroupBy.apply` setting a ``name`` attribute on the result if the result was a :class:`DataFrame` (:issue:`49907`)
21+
- Fixed performance regression in setting with the :meth:`~DataFrame.at` indexer (:issue:`49771`)
2122
-
2223

2324
.. ---------------------------------------------------------------------------

pandas/core/frame.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
)
105105

106106
from pandas.core.dtypes.cast import (
107+
LossySetitemError,
107108
can_hold_element,
108109
construct_1d_arraylike_from_scalar,
109110
construct_2d_arraylike_from_scalar,
@@ -4208,13 +4209,14 @@ def _set_value(
42084209
else:
42094210
icol = self.columns.get_loc(col)
42104211
iindex = self.index.get_loc(index)
4211-
self._mgr.column_setitem(icol, iindex, value)
4212+
self._mgr.column_setitem(icol, iindex, value, inplace=True)
42124213
self._clear_item_cache()
42134214

4214-
except (KeyError, TypeError, ValueError):
4215+
except (KeyError, TypeError, ValueError, LossySetitemError):
42154216
# get_loc might raise a KeyError for missing labels (falling back
42164217
# to (i)loc will do expansion of the index)
4217-
# column_setitem will do validation that may raise TypeError or ValueError
4218+
# column_setitem will do validation that may raise TypeError,
4219+
# ValueError, or LossySetitemError
42184220
# set using a non-recursive method & reset the cache
42194221
if takeable:
42204222
self.iloc[index, col] = value

pandas/core/internals/array_manager.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,9 @@ def iset(
874874
self.arrays[mgr_idx] = value_arr
875875
return
876876

877-
def column_setitem(self, loc: int, idx: int | slice | np.ndarray, value) -> None:
877+
def column_setitem(
878+
self, loc: int, idx: int | slice | np.ndarray, value, inplace: bool = False
879+
) -> None:
878880
"""
879881
Set values ("setitem") into a single column (not setting the full column).
880882
@@ -885,9 +887,12 @@ def column_setitem(self, loc: int, idx: int | slice | np.ndarray, value) -> None
885887
raise TypeError("The column index should be an integer")
886888
arr = self.arrays[loc]
887889
mgr = SingleArrayManager([arr], [self._axes[0]])
888-
new_mgr = mgr.setitem((idx,), value)
889-
# update existing ArrayManager in-place
890-
self.arrays[loc] = new_mgr.arrays[0]
890+
if inplace:
891+
mgr.setitem_inplace(idx, value)
892+
else:
893+
new_mgr = mgr.setitem((idx,), value)
894+
# update existing ArrayManager in-place
895+
self.arrays[loc] = new_mgr.arrays[0]
891896

892897
def insert(self, loc: int, item: Hashable, value: ArrayLike) -> None:
893898
"""

pandas/core/internals/managers.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,9 @@ def _iset_single(
13671367
self._clear_reference_block(blkno)
13681368
return
13691369

1370-
def column_setitem(self, loc: int, idx: int | slice | np.ndarray, value) -> None:
1370+
def column_setitem(
1371+
self, loc: int, idx: int | slice | np.ndarray, value, inplace: bool = False
1372+
) -> None:
13711373
"""
13721374
Set values ("setitem") into a single column (not setting the full column).
13731375
@@ -1385,8 +1387,11 @@ def column_setitem(self, loc: int, idx: int | slice | np.ndarray, value) -> None
13851387
# this manager is only created temporarily to mutate the values in place
13861388
# so don't track references, otherwise the `setitem` would perform CoW again
13871389
col_mgr = self.iget(loc, track_ref=False)
1388-
new_mgr = col_mgr.setitem((idx,), value)
1389-
self.iset(loc, new_mgr._block.values, inplace=True)
1390+
if inplace:
1391+
col_mgr.setitem_inplace(idx, value)
1392+
else:
1393+
new_mgr = col_mgr.setitem((idx,), value)
1394+
self.iset(loc, new_mgr._block.values, inplace=True)
13901395

13911396
def insert(self, loc: int, item: Hashable, value: ArrayLike) -> None:
13921397
"""

0 commit comments

Comments
 (0)