@@ -1564,6 +1564,17 @@ def _convert_to_indexer(self, key, axis: int, is_setter: bool = False):
1564
1564
# -------------------------------------------------------------------
1565
1565
1566
1566
def _setitem_with_indexer (self , indexer , value ):
1567
+ """
1568
+ _setitem_with_indexer is for setting values on a Series/DataFrame
1569
+ using positional indexers.
1570
+
1571
+ If the relevant keys are not present, the Series/DataFrame may be
1572
+ expanded.
1573
+
1574
+ This method is currently broken when dealing with non-unique Indexes,
1575
+ since it goes from positional indexers back to labels when calling
1576
+ BlockManager methods, see GH#12991, GH#22046, GH#15686.
1577
+ """
1567
1578
1568
1579
# also has the side effect of consolidating in-place
1569
1580
from pandas import Series
@@ -1678,24 +1689,19 @@ def _setitem_with_indexer(self, indexer, value):
1678
1689
info_idx = [info_idx ]
1679
1690
labels = item_labels [info_idx ]
1680
1691
1681
- # if we have a partial multiindex, then need to adjust the plane
1682
- # indexer here
1683
- if len (labels ) == 1 and isinstance (
1684
- self .obj [labels [0 ]].axes [0 ], ABCMultiIndex
1685
- ):
1692
+ if len (labels ) == 1 :
1693
+ # We can operate on a single column
1686
1694
item = labels [0 ]
1687
- obj = self .obj [item ]
1688
- index = obj .index
1689
- idx = indexer [:info_axis ][0 ]
1695
+ idx = indexer [0 ]
1690
1696
1691
- plane_indexer = tuple ([idx ]) + indexer [ info_axis + 1 :]
1692
- lplane_indexer = length_of_indexer (plane_indexer [0 ], index )
1697
+ plane_indexer = tuple ([idx ])
1698
+ lplane_indexer = length_of_indexer (plane_indexer [0 ], self . obj . index )
1693
1699
# lplane_indexer gives the expected length of obj[idx]
1694
1700
1695
1701
# require that we are setting the right number of values that
1696
1702
# we are indexing
1697
- if is_list_like_indexer (value ) and lplane_indexer != len (value ):
1698
-
1703
+ if is_list_like_indexer (value ) and 0 != lplane_indexer != len (value ):
1704
+ # Exclude zero-len for e.g. boolean masking that is all-false
1699
1705
raise ValueError (
1700
1706
"cannot set using a multi-index "
1701
1707
"selection indexer with a different "
@@ -1704,12 +1710,11 @@ def _setitem_with_indexer(self, indexer, value):
1704
1710
1705
1711
# non-mi
1706
1712
else :
1707
- plane_indexer = indexer [:info_axis ] + indexer [info_axis + 1 :]
1708
- plane_axis = self .obj .axes [:info_axis ][0 ]
1709
- lplane_indexer = length_of_indexer (plane_indexer [0 ], plane_axis )
1713
+ plane_indexer = indexer [:1 ]
1714
+ lplane_indexer = length_of_indexer (plane_indexer [0 ], self .obj .index )
1710
1715
1711
1716
def setter (item , v ):
1712
- s = self .obj [item ]
1717
+ ser = self .obj [item ]
1713
1718
pi = plane_indexer [0 ] if lplane_indexer == 1 else plane_indexer
1714
1719
1715
1720
# perform the equivalent of a setitem on the info axis
@@ -1721,16 +1726,16 @@ def setter(item, v):
1721
1726
com .is_null_slice (idx ) or com .is_full_slice (idx , len (self .obj ))
1722
1727
for idx in pi
1723
1728
):
1724
- s = v
1729
+ ser = v
1725
1730
else :
1726
1731
# set the item, possibly having a dtype change
1727
- s ._consolidate_inplace ()
1728
- s = s .copy ()
1729
- s ._data = s ._data .setitem (indexer = pi , value = v )
1730
- s ._maybe_update_cacher (clear = True )
1732
+ ser ._consolidate_inplace ()
1733
+ ser = ser .copy ()
1734
+ ser ._data = ser ._data .setitem (indexer = pi , value = v )
1735
+ ser ._maybe_update_cacher (clear = True )
1731
1736
1732
1737
# reset the sliced object if unique
1733
- self .obj [item ] = s
1738
+ self .obj [item ] = ser
1734
1739
1735
1740
# we need an iterable, with a ndim of at least 1
1736
1741
# eg. don't pass through np.array(0)
0 commit comments