34
34
from pandas .core .indexes .api import Index
35
35
36
36
if TYPE_CHECKING :
37
- from pandas import DataFrame # noqa:F401
37
+ from pandas import DataFrame , Series # noqa:F401
38
38
39
39
# "null slice"
40
40
_NS = slice (None , None )
@@ -1543,13 +1543,10 @@ def _setitem_with_indexer(self, indexer, value):
1543
1543
since it goes from positional indexers back to labels when calling
1544
1544
BlockManager methods, see GH#12991, GH#22046, GH#15686.
1545
1545
"""
1546
-
1547
- # also has the side effect of consolidating in-place
1548
- from pandas import Series
1549
-
1550
1546
info_axis = self .obj ._info_axis_number
1551
1547
1552
1548
# maybe partial set
1549
+ # _is_mixed_type has the side effect of consolidating in-place
1553
1550
take_split_path = self .obj ._is_mixed_type
1554
1551
1555
1552
# if there is only one block/type, still have to take split path
@@ -1642,6 +1639,8 @@ def _setitem_with_indexer(self, indexer, value):
1642
1639
1643
1640
# align and set the values
1644
1641
if take_split_path :
1642
+ # We have to operate column-wise
1643
+
1645
1644
# Above we only set take_split_path to True for 2D cases
1646
1645
assert self .ndim == 2
1647
1646
assert info_axis == 1
@@ -1682,29 +1681,6 @@ def _setitem_with_indexer(self, indexer, value):
1682
1681
1683
1682
pi = plane_indexer [0 ] if lplane_indexer == 1 else plane_indexer
1684
1683
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
-
1708
1684
# we need an iterable, with a ndim of at least 1
1709
1685
# eg. don't pass through np.array(0)
1710
1686
if is_list_like_indexer (value ) and getattr (value , "ndim" , 1 ) > 0 :
@@ -1725,7 +1701,7 @@ def isetter(loc, v):
1725
1701
else :
1726
1702
v = np .nan
1727
1703
1728
- isetter (loc , v )
1704
+ self . _setitem_single_column (loc , v , pi )
1729
1705
1730
1706
# we have an equal len ndarray/convertible to our labels
1731
1707
# hasattr first, to avoid coercing to ndarray without reason.
@@ -1744,7 +1720,7 @@ def isetter(loc, v):
1744
1720
1745
1721
for i , loc in enumerate (ilocs ):
1746
1722
# setting with a list, re-coerces
1747
- isetter (loc , value [:, i ].tolist ())
1723
+ self . _setitem_single_column (loc , value [:, i ].tolist (), pi )
1748
1724
1749
1725
elif (
1750
1726
len (labels ) == 1
@@ -1753,7 +1729,7 @@ def isetter(loc, v):
1753
1729
):
1754
1730
# we have an equal len list/ndarray
1755
1731
# We only get here with len(labels) == len(ilocs) == 1
1756
- isetter (ilocs [0 ], value )
1732
+ self . _setitem_single_column (ilocs [0 ], value , pi )
1757
1733
1758
1734
elif lplane_indexer == 0 and len (value ) == len (self .obj .index ):
1759
1735
# We get here in one case via .loc with a all-False mask
@@ -1768,50 +1744,87 @@ def isetter(loc, v):
1768
1744
)
1769
1745
1770
1746
for loc , v in zip (ilocs , value ):
1771
- isetter (loc , v )
1747
+ self . _setitem_single_column (loc , v , pi )
1772
1748
else :
1773
1749
1774
1750
# scalar value
1775
1751
for loc in ilocs :
1776
- isetter (loc , value )
1752
+ self . _setitem_single_column (loc , value , pi )
1777
1753
1778
1754
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 )
1780
1778
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 )
1796
1781
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
1798
1788
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 )
1804
1791
1805
- elif isinstance (value , ABCDataFrame ):
1806
- value = self ._align_frame (indexer , value )
1792
+ if isinstance (indexer , tuple ):
1807
1793
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
1810
1809
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 )
1815
1828
1816
1829
def _setitem_with_indexer_missing (self , indexer , value ):
1817
1830
"""
@@ -1873,7 +1886,7 @@ def _setitem_with_indexer_missing(self, indexer, value):
1873
1886
self .obj ._mgr = self .obj .append (value )._mgr
1874
1887
self .obj ._maybe_update_cacher (clear = True )
1875
1888
1876
- def _align_series (self , indexer , ser : ABCSeries , multiindex_indexer : bool = False ):
1889
+ def _align_series (self , indexer , ser : "Series" , multiindex_indexer : bool = False ):
1877
1890
"""
1878
1891
Parameters
1879
1892
----------
0 commit comments