Skip to content

Commit f388d99

Browse files
authored
REF: simplify _setitem_with_indexer (#62366)
1 parent e209a35 commit f388d99

File tree

2 files changed

+73
-71
lines changed

2 files changed

+73
-71
lines changed

pandas/core/indexing.py

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,21 +1813,10 @@ def _get_setitem_indexer(self, key):
18131813

18141814
# -------------------------------------------------------------------
18151815

1816-
def _setitem_with_indexer(self, indexer, value, name: str = "iloc") -> None:
1816+
def _decide_split_path(self, indexer, value) -> bool:
18171817
"""
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.
18271819
"""
1828-
info_axis = self.obj._info_axis_number
1829-
1830-
# maybe partial set
18311820
take_split_path = not self.obj._mgr.is_single_block
18321821

18331822
if not take_split_path and isinstance(value, ABCDataFrame):
@@ -1855,77 +1844,88 @@ def _setitem_with_indexer(self, indexer, value, name: str = "iloc") -> None:
18551844
take_split_path = True
18561845
break
18571846

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+
18581910
if isinstance(indexer, tuple):
18591911
nindexer = []
18601912
for i, idx in enumerate(indexer):
1861-
if isinstance(idx, dict):
1913+
idx, missing = convert_missing_indexer(idx)
1914+
if missing:
18621915
# reindex the axis to the new value
18631916
# and set inplace
1864-
key, _ = convert_missing_indexer(idx)
1917+
key = idx
18651918

18661919
# if this is the items axes, then take the main missing
18671920
# path first
1868-
# this correctly sets the dtype and avoids cache issues
1921+
# this correctly sets the dtype
18691922
# essentially this separates out the block that is needed
18701923
# to possibly be modified
18711924
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)
19231926
return
19241927

19251928
# 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
19291929
index = self.obj._get_axis(i)
19301930
labels = index.insert(len(index), key)
19311931

@@ -2722,7 +2722,7 @@ def convert_missing_indexer(indexer):
27222722
return indexer, False
27232723

27242724

2725-
def convert_from_missing_indexer_tuple(indexer, axes):
2725+
def convert_from_missing_indexer_tuple(indexer: tuple, axes: list[Index]) -> tuple:
27262726
"""
27272727
Create a filtered indexer that doesn't have any missing indexers.
27282728
"""

pandas/util/_decorators.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ def deprecate_kwarg(
177177
def _deprecate_kwarg(func: F) -> F:
178178
@wraps(func)
179179
def wrapper(*args, **kwargs) -> Callable[..., Any]:
180+
__tracebackhide__ = True
181+
180182
old_arg_value = kwargs.pop(old_arg_name, None)
181183

182184
if old_arg_value is not None:

0 commit comments

Comments
 (0)