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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ Indexing
- Bug in :meth:`DataFrame.drop` where the error message did not show missing labels with commas when raising ``KeyError`` (:issue:`42881`)
- Bug in :meth:`DataFrame.query` where method calls in query strings led to errors when the ``numexpr`` package was installed. (:issue:`22435`)
- Bug in :meth:`DataFrame.nlargest` and :meth:`Series.nlargest` where sorted result did not count indexes containing ``np.nan`` (:issue:`28984`)
- Bug in :meth:`DataFrame.dropna` where :class:`RangeIndex` loses its type even when nothing is dropped (:issue:`41965`)
- Bug in indexing on a non-unique object-dtype :class:`Index` with an NA scalar (e.g. ``np.nan``) (:issue:`43711`)
-

Expand Down
21 changes: 18 additions & 3 deletions pandas/core/indexes/range.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
import numpy as np

from pandas._libs import index as libindex
from pandas._libs.lib import no_default
from pandas._libs.lib import (
maybe_indices_to_slice,
no_default,
)
from pandas._typing import (
Dtype,
npt,
Expand Down Expand Up @@ -433,8 +436,19 @@ def delete(self, loc) -> Int64Index: # type: ignore[override]

def take(
self, indices, axis: int = 0, allow_fill: bool = True, fill_value=None, **kwargs
) -> Int64Index:
) -> Int64Index | RangeIndex:
with rewrite_exception("Int64Index", type(self).__name__):
if (
fill_value is None
and isinstance(indices, np.ndarray)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is way too complicated and likely hiding bugs. @jbrockmendel can you suggest anything here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @phofl

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

take a look at what we do in DatetimeTimedeltaMixin.take; should be possible to do this without the try/except

and indices.dtype == int
):
try:
slc = maybe_indices_to_slice(indices, len(indices))
return self[slc]
except ValueError:
# Buffer dtype mismatch, expected 'intp_t' but got something else
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when does this raise (and you are catching)?

Copy link
Contributor Author

@debnathshoham debnathshoham Sep 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was raising in cases where I was not getting a slice (either because indices was BlockPlacement -> TypeError, or when indices contained anything other than intp_t -> ValueError).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you just do something like

if isinstance(indices, slice): ..... 

to avoid the try/except

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried to do that.. but there can be a lot of possibilities of the contents of indices, and that's where the error is thrown

return self._int64index.take(
indices,
axis=axis,
Expand Down Expand Up @@ -770,7 +784,8 @@ def _concat(self, indexes: list[Index], name: Hashable) -> Index:
if start is None:
# This is set by the first non-empty index
start = rng.start
if step is None and len(rng) > 1:
if step is None and (len(rng) > 1 or rng.step < 0):
# GH #41965 for decreasing RangeIndex, like range(0, -1, -1)
step = rng.step
elif step is None:
# First non-empty index had only one element
Expand Down
6 changes: 6 additions & 0 deletions pandas/tests/frame/methods/test_dropna.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,9 @@ def test_dropna_pos_args_deprecation(self):
result = df.dropna(1)
expected = DataFrame({"a": [1, 2, 3]})
tm.assert_frame_equal(result, expected)

def test_dropna_retains_RangeIndex_when_nothing_dropped(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RangeIndex -> rangeindex

# GH#41965
expected = DataFrame({"a": range(10)})
result = expected.dropna()
tm.assert_frame_equal(result, expected, check_index_type=True)
13 changes: 13 additions & 0 deletions pandas/tests/reshape/concat/test_concat.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,3 +698,16 @@ def test_concat_posargs_deprecation():
result = concat([df, df2], 0)
expected = DataFrame([[1, 2, 3], [4, 5, 6]], index=["a", "b"])
tm.assert_frame_equal(result, expected)


@pytest.mark.parametrize(
"ser2_index", [pd.RangeIndex(0, -1, -1), pd.RangeIndex(0, 1, 1)]
)
def test_concat_series_with_rangeindex(ser2_index):
# GH#41965
ser1 = Series([], index=pd.RangeIndex(0), dtype="int64")
ser2 = Series([0], index=ser2_index, dtype="int64")
# expected should be exactly ser2, as ser1 is empty
expected = ser2.copy()
result = concat([ser1, ser2])
tm.assert_series_equal(expected, result, check_index_type=True)