Skip to content

Commit 09e5238

Browse files
jbrockmendelKevin D Smith
authored and
Kevin D Smith
committed
REF: separate out helpers from iLoc._setitem_with_indexer (pandas-dev#36315)
1 parent aa4d86a commit 09e5238

File tree

1 file changed

+77
-64
lines changed

1 file changed

+77
-64
lines changed

pandas/core/indexing.py

+77-64
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from pandas.core.indexes.api import Index
3535

3636
if TYPE_CHECKING:
37-
from pandas import DataFrame # noqa:F401
37+
from pandas import DataFrame, Series # noqa:F401
3838

3939
# "null slice"
4040
_NS = slice(None, None)
@@ -1543,13 +1543,10 @@ def _setitem_with_indexer(self, indexer, value):
15431543
since it goes from positional indexers back to labels when calling
15441544
BlockManager methods, see GH#12991, GH#22046, GH#15686.
15451545
"""
1546-
1547-
# also has the side effect of consolidating in-place
1548-
from pandas import Series
1549-
15501546
info_axis = self.obj._info_axis_number
15511547

15521548
# maybe partial set
1549+
# _is_mixed_type has the side effect of consolidating in-place
15531550
take_split_path = self.obj._is_mixed_type
15541551

15551552
# if there is only one block/type, still have to take split path
@@ -1642,6 +1639,8 @@ def _setitem_with_indexer(self, indexer, value):
16421639

16431640
# align and set the values
16441641
if take_split_path:
1642+
# We have to operate column-wise
1643+
16451644
# Above we only set take_split_path to True for 2D cases
16461645
assert self.ndim == 2
16471646
assert info_axis == 1
@@ -1682,29 +1681,6 @@ def _setitem_with_indexer(self, indexer, value):
16821681

16831682
pi = plane_indexer[0] if lplane_indexer == 1 else plane_indexer
16841683

1685-
def isetter(loc, v):
1686-
# positional setting on column loc
1687-
ser = self.obj._ixs(loc, axis=1)
1688-
1689-
# perform the equivalent of a setitem on the info axis
1690-
# as we have a null slice or a slice with full bounds
1691-
# which means essentially reassign to the columns of a
1692-
# multi-dim object
1693-
# GH6149 (null slice), GH10408 (full bounds)
1694-
if isinstance(pi, tuple) and all(
1695-
com.is_null_slice(idx) or com.is_full_slice(idx, len(self.obj))
1696-
for idx in pi
1697-
):
1698-
ser = v
1699-
else:
1700-
# set the item, possibly having a dtype change
1701-
ser = ser.copy()
1702-
ser._mgr = ser._mgr.setitem(indexer=pi, value=v)
1703-
ser._maybe_update_cacher(clear=True)
1704-
1705-
# reset the sliced object if unique
1706-
self.obj._iset_item(loc, ser)
1707-
17081684
# we need an iterable, with a ndim of at least 1
17091685
# eg. don't pass through np.array(0)
17101686
if is_list_like_indexer(value) and getattr(value, "ndim", 1) > 0:
@@ -1725,7 +1701,7 @@ def isetter(loc, v):
17251701
else:
17261702
v = np.nan
17271703

1728-
isetter(loc, v)
1704+
self._setitem_single_column(loc, v, pi)
17291705

17301706
# we have an equal len ndarray/convertible to our labels
17311707
# hasattr first, to avoid coercing to ndarray without reason.
@@ -1744,7 +1720,7 @@ def isetter(loc, v):
17441720

17451721
for i, loc in enumerate(ilocs):
17461722
# setting with a list, re-coerces
1747-
isetter(loc, value[:, i].tolist())
1723+
self._setitem_single_column(loc, value[:, i].tolist(), pi)
17481724

17491725
elif (
17501726
len(labels) == 1
@@ -1753,7 +1729,7 @@ def isetter(loc, v):
17531729
):
17541730
# we have an equal len list/ndarray
17551731
# We only get here with len(labels) == len(ilocs) == 1
1756-
isetter(ilocs[0], value)
1732+
self._setitem_single_column(ilocs[0], value, pi)
17571733

17581734
elif lplane_indexer == 0 and len(value) == len(self.obj.index):
17591735
# We get here in one case via .loc with a all-False mask
@@ -1768,50 +1744,87 @@ def isetter(loc, v):
17681744
)
17691745

17701746
for loc, v in zip(ilocs, value):
1771-
isetter(loc, v)
1747+
self._setitem_single_column(loc, v, pi)
17721748
else:
17731749

17741750
# scalar value
17751751
for loc in ilocs:
1776-
isetter(loc, value)
1752+
self._setitem_single_column(loc, value, pi)
17771753

17781754
else:
1779-
if isinstance(indexer, tuple):
1755+
self._setitem_single_block_inplace(indexer, value)
1756+
1757+
def _setitem_single_column(self, loc: int, value, plane_indexer):
1758+
# positional setting on column loc
1759+
pi = plane_indexer
1760+
1761+
ser = self.obj._ixs(loc, axis=1)
1762+
1763+
# perform the equivalent of a setitem on the info axis
1764+
# as we have a null slice or a slice with full bounds
1765+
# which means essentially reassign to the columns of a
1766+
# multi-dim object
1767+
# GH#6149 (null slice), GH#10408 (full bounds)
1768+
if isinstance(pi, tuple) and all(
1769+
com.is_null_slice(idx) or com.is_full_slice(idx, len(self.obj))
1770+
for idx in pi
1771+
):
1772+
ser = value
1773+
else:
1774+
# set the item, possibly having a dtype change
1775+
ser = ser.copy()
1776+
ser._mgr = ser._mgr.setitem(indexer=pi, value=value)
1777+
ser._maybe_update_cacher(clear=True)
17801778

1781-
# if we are setting on the info axis ONLY
1782-
# set using those methods to avoid block-splitting
1783-
# logic here
1784-
if (
1785-
len(indexer) > info_axis
1786-
and is_integer(indexer[info_axis])
1787-
and all(
1788-
com.is_null_slice(idx)
1789-
for i, idx in enumerate(indexer)
1790-
if i != info_axis
1791-
)
1792-
and item_labels.is_unique
1793-
):
1794-
self.obj[item_labels[indexer[info_axis]]] = value
1795-
return
1779+
# reset the sliced object if unique
1780+
self.obj._iset_item(loc, ser)
17961781

1797-
indexer = maybe_convert_ix(*indexer)
1782+
def _setitem_single_block_inplace(self, indexer, value):
1783+
"""
1784+
_setitem_with_indexer for the case when we have a single Block
1785+
and the value can be set into it without casting.
1786+
"""
1787+
from pandas import Series
17981788

1799-
if isinstance(value, (ABCSeries, dict)):
1800-
# TODO(EA): ExtensionBlock.setitem this causes issues with
1801-
# setting for extensionarrays that store dicts. Need to decide
1802-
# if it's worth supporting that.
1803-
value = self._align_series(indexer, Series(value))
1789+
info_axis = self.obj._info_axis_number
1790+
item_labels = self.obj._get_axis(info_axis)
18041791

1805-
elif isinstance(value, ABCDataFrame):
1806-
value = self._align_frame(indexer, value)
1792+
if isinstance(indexer, tuple):
18071793

1808-
# check for chained assignment
1809-
self.obj._check_is_chained_assignment_possible()
1794+
# if we are setting on the info axis ONLY
1795+
# set using those methods to avoid block-splitting
1796+
# logic here
1797+
if (
1798+
len(indexer) > info_axis
1799+
and is_integer(indexer[info_axis])
1800+
and all(
1801+
com.is_null_slice(idx)
1802+
for i, idx in enumerate(indexer)
1803+
if i != info_axis
1804+
)
1805+
and item_labels.is_unique
1806+
):
1807+
self.obj[item_labels[indexer[info_axis]]] = value
1808+
return
18101809

1811-
# actually do the set
1812-
self.obj._consolidate_inplace()
1813-
self.obj._mgr = self.obj._mgr.setitem(indexer=indexer, value=value)
1814-
self.obj._maybe_update_cacher(clear=True)
1810+
indexer = maybe_convert_ix(*indexer)
1811+
1812+
if isinstance(value, (ABCSeries, dict)):
1813+
# TODO(EA): ExtensionBlock.setitem this causes issues with
1814+
# setting for extensionarrays that store dicts. Need to decide
1815+
# if it's worth supporting that.
1816+
value = self._align_series(indexer, Series(value))
1817+
1818+
elif isinstance(value, ABCDataFrame):
1819+
value = self._align_frame(indexer, value)
1820+
1821+
# check for chained assignment
1822+
self.obj._check_is_chained_assignment_possible()
1823+
1824+
# actually do the set
1825+
self.obj._consolidate_inplace()
1826+
self.obj._mgr = self.obj._mgr.setitem(indexer=indexer, value=value)
1827+
self.obj._maybe_update_cacher(clear=True)
18151828

18161829
def _setitem_with_indexer_missing(self, indexer, value):
18171830
"""
@@ -1873,7 +1886,7 @@ def _setitem_with_indexer_missing(self, indexer, value):
18731886
self.obj._mgr = self.obj.append(value)._mgr
18741887
self.obj._maybe_update_cacher(clear=True)
18751888

1876-
def _align_series(self, indexer, ser: ABCSeries, multiindex_indexer: bool = False):
1889+
def _align_series(self, indexer, ser: "Series", multiindex_indexer: bool = False):
18771890
"""
18781891
Parameters
18791892
----------

0 commit comments

Comments
 (0)