Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 45 additions & 29 deletions pandas/core/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1087,37 +1087,53 @@ def _getitem_lowerdim(self, tup: tuple):

tup = self._validate_key_length(tup)

# Reverse tuple so that we are indexing along columns before rows
# For scalar row + slice columns, process by first getting the column slice
# to preserve dtypes, then extracting the row
# Otherwise, reverse tuple so that we are indexing along columns before rows
# and avoid unintended dtype inference. # GH60600
for i, key in zip(range(len(tup) - 1, -1, -1), reversed(tup), strict=True):
if is_label_like(key) or is_list_like(key):
# We don't need to check for tuples here because those are
# caught by the _is_nested_tuple_indexer check above.
section = self._getitem_axis(key, axis=i)

# We should never have a scalar section here, because
# _getitem_lowerdim is only called after a check for
# is_scalar_access, which that would be.
if section.ndim == self.ndim:
# we're in the middle of slicing through a MultiIndex
# revise the key wrt to `section` by inserting an _NS
new_key = tup[:i] + (_NS,) + tup[i + 1 :]
if len(tup) == 2 and is_scalar(tup[0]) and isinstance(tup[1], slice):
# Handle scalar row + slice columns case to preserve dtypes
row_key, col_slice = tup[0], tup[1]
# First, get the column slice to preserve dtypes
col_section = self._getitem_axis(col_slice, axis=1)
# Then get the specific row from this sub-DataFrame
if self.name == "iloc":
result = col_section.iloc[row_key]
else:
result = col_section.loc[row_key]
return result
else:
# Reverse tuple so that we are indexing along columns before rows
# and avoid unintended dtype inference. # GH60600
for i, key in zip(range(len(tup) - 1, -1, -1), reversed(tup), strict=True):
if is_label_like(key) or is_list_like(key):
# We don't need to check for tuples here because those are
# caught by the _is_nested_tuple_indexer check above.
section = self._getitem_axis(key, axis=i)

# We should never have a scalar section here, because
# _getitem_lowerdim is only called after a check for
# is_scalar_access, which that would be.
if section.ndim == self.ndim:
# we're in the middle of slicing through a MultiIndex
# revise the key wrt to `section` by inserting an _NS
new_key = tup[:i] + (_NS,) + tup[i + 1 :]

else:
# Note: the section.ndim == self.ndim check above
# rules out having DataFrame here, so we dont need to worry
# about transposing.
new_key = tup[:i] + tup[i + 1 :]

if len(new_key) == 1:
new_key = new_key[0]

# Slices should return views, but calling iloc/loc with a null
# slice returns a new object.
if com.is_null_slice(new_key):
return section
# This is an elided recursive call to iloc/loc
return getattr(section, self.name)[new_key]
else:
# Note: the section.ndim == self.ndim check above
# rules out having DataFrame here, so we dont need to worry
# about transposing.
new_key = tup[:i] + tup[i + 1 :]

if len(new_key) == 1:
new_key = new_key[0]

# Slices should return views, but calling iloc/loc with a null
# slice returns a new object.
if com.is_null_slice(new_key):
return section
# This is an elided recursive call to iloc/loc
return getattr(section, self.name)[new_key]

raise IndexingError("not applicable")

Expand Down
7 changes: 7 additions & 0 deletions pandas/tests/indexing/test_iloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1548,3 +1548,10 @@ def test_setitem_pyarrow_int_series(self):

expected = Series([7, 8, 3], dtype="int64[pyarrow]")
tm.assert_series_equal(ser, expected)

def test_iloc_scalar_row_slice_columns_dtype(self):
# GH 63071
df = DataFrame([["a", 1.0, 2.0], ["b", 3.0, 4.0]])
result = df.iloc[0, 1:]
expected = Series([1.0, 2.0], index=[1, 2], dtype=float, name=0)
tm.assert_series_equal(result, expected)
8 changes: 8 additions & 0 deletions pandas/tests/indexing/test_loc.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ def test_loc_dtype():
tm.assert_series_equal(result, expected)


def test_loc_scalar_row_slice_columns_dtype():
# GH 63071
df = DataFrame([["a", 1.0, 2.0], ["b", 3.0, 4.0]])
result = df.loc[0, 1:]
expected = Series([1.0, 2.0], index=[1, 2], dtype=float, name=0)
tm.assert_series_equal(result, expected)


class TestLoc:
def test_none_values_on_string_columns(self, using_infer_string):
# Issue #32218
Expand Down
Loading