@@ -1813,21 +1813,10 @@ def _get_setitem_indexer(self, key):
1813
1813
1814
1814
# -------------------------------------------------------------------
1815
1815
1816
- def _setitem_with_indexer (self , indexer , value , name : str = "iloc" ) -> None :
1816
+ def _decide_split_path (self , indexer , value ) -> bool :
1817
1817
"""
1818
- _setitem_with_indexer is for setting values on a Series/DataFrame
1819
- using positional indexers.
1820
-
1821
- If the relevant keys are not present, the Series/DataFrame may be
1822
- expanded.
1823
-
1824
- This method is currently broken when dealing with non-unique Indexes,
1825
- since it goes from positional indexers back to labels when calling
1826
- BlockManager methods, see GH#12991, GH#22046, GH#15686.
1818
+ Decide whether we will take a block-by-block path.
1827
1819
"""
1828
- info_axis = self .obj ._info_axis_number
1829
-
1830
- # maybe partial set
1831
1820
take_split_path = not self .obj ._mgr .is_single_block
1832
1821
1833
1822
if not take_split_path and isinstance (value , ABCDataFrame ):
@@ -1855,77 +1844,88 @@ def _setitem_with_indexer(self, indexer, value, name: str = "iloc") -> None:
1855
1844
take_split_path = True
1856
1845
break
1857
1846
1847
+ return take_split_path
1848
+
1849
+ def _setitem_new_column (self , indexer , key , value , name : str ) -> None :
1850
+ """
1851
+ _setitem_with_indexer cases that can go through DataFrame.__setitem__.
1852
+ """
1853
+ # add the new item, and set the value
1854
+ # must have all defined axes if we have a scalar
1855
+ # or a list-like on the non-info axes if we have a
1856
+ # list-like
1857
+ if not len (self .obj ):
1858
+ if not is_list_like_indexer (value ):
1859
+ raise ValueError (
1860
+ "cannot set a frame with no defined index and a scalar"
1861
+ )
1862
+ self .obj [key ] = value
1863
+ return
1864
+
1865
+ # add a new item with the dtype setup
1866
+ if com .is_null_slice (indexer [0 ]):
1867
+ # We are setting an entire column
1868
+ self .obj [key ] = value
1869
+ return
1870
+ elif is_array_like (value ):
1871
+ # GH#42099
1872
+ arr = extract_array (value , extract_numpy = True )
1873
+ taker = - 1 * np .ones (len (self .obj ), dtype = np .intp )
1874
+ empty_value = algos .take_nd (arr , taker )
1875
+ if not isinstance (value , ABCSeries ):
1876
+ # if not Series (in which case we need to align),
1877
+ # we can short-circuit
1878
+ if isinstance (arr , np .ndarray ) and arr .ndim == 1 and len (arr ) == 1 :
1879
+ # NumPy 1.25 deprecation: https://github.com/numpy/numpy/pull/10615
1880
+ arr = arr [0 , ...]
1881
+ empty_value [indexer [0 ]] = arr
1882
+ self .obj [key ] = empty_value
1883
+ return
1884
+
1885
+ self .obj [key ] = empty_value
1886
+ elif not is_list_like (value ):
1887
+ self .obj [key ] = construct_1d_array_from_inferred_fill_value (
1888
+ value , len (self .obj )
1889
+ )
1890
+ else :
1891
+ # FIXME: GH#42099#issuecomment-864326014
1892
+ self .obj [key ] = infer_fill_value (value )
1893
+
1894
+ new_indexer = convert_from_missing_indexer_tuple (indexer , self .obj .axes )
1895
+ self ._setitem_with_indexer (new_indexer , value , name )
1896
+
1897
+ return
1898
+
1899
+ def _setitem_with_indexer (self , indexer , value , name : str = "iloc" ) -> None :
1900
+ """
1901
+ _setitem_with_indexer is for setting values on a Series/DataFrame
1902
+ using positional indexers.
1903
+
1904
+ If the relevant keys are not present, the Series/DataFrame may be
1905
+ expanded.
1906
+ """
1907
+ info_axis = self .obj ._info_axis_number
1908
+ take_split_path = self ._decide_split_path (indexer , value )
1909
+
1858
1910
if isinstance (indexer , tuple ):
1859
1911
nindexer = []
1860
1912
for i , idx in enumerate (indexer ):
1861
- if isinstance (idx , dict ):
1913
+ idx , missing = convert_missing_indexer (idx )
1914
+ if missing :
1862
1915
# reindex the axis to the new value
1863
1916
# and set inplace
1864
- key , _ = convert_missing_indexer ( idx )
1917
+ key = idx
1865
1918
1866
1919
# if this is the items axes, then take the main missing
1867
1920
# path first
1868
- # this correctly sets the dtype and avoids cache issues
1921
+ # this correctly sets the dtype
1869
1922
# essentially this separates out the block that is needed
1870
1923
# to possibly be modified
1871
1924
if self .ndim > 1 and i == info_axis :
1872
- # add the new item, and set the value
1873
- # must have all defined axes if we have a scalar
1874
- # or a list-like on the non-info axes if we have a
1875
- # list-like
1876
- if not len (self .obj ):
1877
- if not is_list_like_indexer (value ):
1878
- raise ValueError (
1879
- "cannot set a frame with no "
1880
- "defined index and a scalar"
1881
- )
1882
- self .obj [key ] = value
1883
- return
1884
-
1885
- # add a new item with the dtype setup
1886
- if com .is_null_slice (indexer [0 ]):
1887
- # We are setting an entire column
1888
- self .obj [key ] = value
1889
- return
1890
- elif is_array_like (value ):
1891
- # GH#42099
1892
- arr = extract_array (value , extract_numpy = True )
1893
- taker = - 1 * np .ones (len (self .obj ), dtype = np .intp )
1894
- empty_value = algos .take_nd (arr , taker )
1895
- if not isinstance (value , ABCSeries ):
1896
- # if not Series (in which case we need to align),
1897
- # we can short-circuit
1898
- if (
1899
- isinstance (arr , np .ndarray )
1900
- and arr .ndim == 1
1901
- and len (arr ) == 1
1902
- ):
1903
- # NumPy 1.25 deprecation: https://github.com/numpy/numpy/pull/10615
1904
- arr = arr [0 , ...]
1905
- empty_value [indexer [0 ]] = arr
1906
- self .obj [key ] = empty_value
1907
- return
1908
-
1909
- self .obj [key ] = empty_value
1910
- elif not is_list_like (value ):
1911
- self .obj [key ] = construct_1d_array_from_inferred_fill_value (
1912
- value , len (self .obj )
1913
- )
1914
- else :
1915
- # FIXME: GH#42099#issuecomment-864326014
1916
- self .obj [key ] = infer_fill_value (value )
1917
-
1918
- new_indexer = convert_from_missing_indexer_tuple (
1919
- indexer , self .obj .axes
1920
- )
1921
- self ._setitem_with_indexer (new_indexer , value , name )
1922
-
1925
+ self ._setitem_new_column (indexer , key , value , name = name )
1923
1926
return
1924
1927
1925
1928
# reindex the axis
1926
- # make sure to clear the cache because we are
1927
- # just replacing the block manager here
1928
- # so the object is the same
1929
1929
index = self .obj ._get_axis (i )
1930
1930
labels = index .insert (len (index ), key )
1931
1931
@@ -2722,7 +2722,7 @@ def convert_missing_indexer(indexer):
2722
2722
return indexer , False
2723
2723
2724
2724
2725
- def convert_from_missing_indexer_tuple (indexer , axes ) :
2725
+ def convert_from_missing_indexer_tuple (indexer : tuple , axes : list [ Index ]) -> tuple :
2726
2726
"""
2727
2727
Create a filtered indexer that doesn't have any missing indexers.
2728
2728
"""
0 commit comments