Skip to content

PERF: Unary methods on RangeIndex returns RangeIndex #57825

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ Performance improvements
- Performance improvement in :meth:`RangeIndex.take` returning a :class:`RangeIndex` instead of a :class:`Index` when possible. (:issue:`57445`, :issue:`57752`)
- Performance improvement in ``DataFrameGroupBy.__len__`` and ``SeriesGroupBy.__len__`` (:issue:`57595`)
- Performance improvement in indexing operations for string dtypes (:issue:`56997`)
- Performance improvement in unary methods on a :class:`RangeIndex` returning a :class:`RangeIndex` instead of a :class:`Index` when possible. (:issue:`57825`)

.. ---------------------------------------------------------------------------
.. _whatsnew_300.bug_fixes:
Expand Down
21 changes: 21 additions & 0 deletions pandas/core/indexes/range.py
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,27 @@ def _arith_method(self, other, op):
# test_arithmetic_explicit_conversions
return super()._arith_method(other, op)

def __abs__(self) -> Self | Index:
if len(self) == 0 or self.min() >= 0:
return self.copy()
elif self.max() <= 0:
return -self
else:
return super().__abs__()

def __neg__(self) -> Self:
rng = range(-self.start, -self.stop, -self.step)
return self._simple_new(rng, name=self.name)

def __pos__(self) -> Self:
return self.copy()

def __invert__(self) -> Self:
if len(self) == 0:
return self.copy()
rng = range(~self.start, ~self.stop, -self.step)
return self._simple_new(rng, name=self.name)

# error: Return type "Index" of "take" incompatible with return type
# "RangeIndex" in supertype "Index"
def take( # type: ignore[override]
Expand Down
61 changes: 61 additions & 0 deletions pandas/tests/indexes/ranges/test_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,67 @@ def test_getitem_boolmask_wrong_length():
ri[[True]]


def test_pos_returns_rangeindex():
ri = RangeIndex(2, name="foo")
expected = ri.copy()
result = +ri
tm.assert_index_equal(result, expected, exact=True)


def test_neg_returns_rangeindex():
ri = RangeIndex(2, name="foo")
result = -ri
expected = RangeIndex(0, -2, -1, name="foo")
tm.assert_index_equal(result, expected, exact=True)

ri = RangeIndex(-2, 2, name="foo")
result = -ri
expected = RangeIndex(2, -2, -1, name="foo")
tm.assert_index_equal(result, expected, exact=True)


@pytest.mark.parametrize(
"rng, exp_rng",
[
[range(0), range(0)],
[range(10), range(10)],
[range(-2, 1, 1), range(2, -1, -1)],
[range(0, -10, -1), range(0, 10, 1)],
],
)
def test_abs_returns_rangeindex(rng, exp_rng):
ri = RangeIndex(rng, name="foo")
expected = RangeIndex(exp_rng, name="foo")
result = abs(ri)
tm.assert_index_equal(result, expected, exact=True)


def test_abs_returns_index():
ri = RangeIndex(-2, 2, name="foo")
result = abs(ri)
expected = Index([2, 1, 0, 1], name="foo")
tm.assert_index_equal(result, expected, exact=True)


@pytest.mark.parametrize(
"rng",
[
range(0),
range(5),
range(0, -5, -1),
range(-2, 2, 1),
range(2, -2, -2),
range(0, 5, 2),
],
)
def test_invert_returns_rangeindex(rng):
ri = RangeIndex(rng, name="foo")
result = ~ri
assert isinstance(result, RangeIndex)
expected = ~Index(list(rng), name="foo")
tm.assert_index_equal(result, expected, exact=False)


def test_getitem_integers_return_rangeindex():
result = RangeIndex(0, 10, 2, name="foo")[[0, -1]]
expected = RangeIndex(start=0, stop=16, step=8, name="foo")
Expand Down