Skip to content

Commit

Permalink
PERF: Implement RangeIndex min/max using RangeIndex properties (#17611)
Browse files Browse the repository at this point in the history
  • Loading branch information
jschendel authored and jreback committed Sep 22, 2017
1 parent 9732af2 commit 26681db
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 1 deletion.
20 changes: 20 additions & 0 deletions asv_bench/benchmarks/index_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,23 @@ def time_datetime_level_values_full(self):

def time_datetime_level_values_sliced(self):
self.mi[:10].values


class Range(object):
goal_time = 0.2

def setup(self):
self.idx_inc = RangeIndex(start=0, stop=10**7, step=3)
self.idx_dec = RangeIndex(start=10**7, stop=-1, step=-3)

def time_max(self):
self.idx_inc.max()

def time_max_trivial(self):
self.idx_dec.max()

def time_min(self):
self.idx_dec.min()

def time_min_trivial(self):
self.idx_inc.min()
14 changes: 14 additions & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,20 @@ Selecting
Index.slice_indexer
Index.slice_locs

.. _api.numericindex:

Numeric Index
-------------

.. autosummary::
:toctree: generated/
:template: autosummary/class_without_autosummary.rst

RangeIndex
Int64Index
UInt64Index
Float64Index

.. _api.categoricalindex:

CategoricalIndex
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.21.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ Performance Improvements
- Improved performance of :meth:`Categorical.set_categories` by not materializing the values (:issue:`17508`)
- :attr:`Timestamp.microsecond` no longer re-computes on attribute access (:issue:`17331`)
- Improved performance of the :class:`CategoricalIndex` for data that is already categorical dtype (:issue:`17513`)
- Improved performance of :meth:`RangeIndex.min` and :meth:`RangeIndex.max` by using ``RangeIndex`` properties to perform the computations (:issue:`17607`)

.. _whatsnew_0210.bug_fixes:

Expand Down
18 changes: 18 additions & 0 deletions pandas/core/indexes/range.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,24 @@ def copy(self, name=None, deep=False, dtype=None, **kwargs):
return RangeIndex(name=name, fastpath=True,
**dict(self._get_data_as_items()))

def _minmax(self, meth):
no_steps = len(self) - 1
if no_steps == -1:
return np.nan
elif ((meth == 'min' and self._step > 0) or
(meth == 'max' and self._step < 0)):
return self._start

return self._start + self._step * no_steps

def min(self):
"""The minimum value of the RangeIndex"""
return self._minmax('min')

def max(self):
"""The maximum value of the RangeIndex"""
return self._minmax('max')

def argsort(self, *args, **kwargs):
"""
Returns the indices that would sort the index and its
Expand Down
21 changes: 20 additions & 1 deletion pandas/tests/indexes/test_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import numpy as np

from pandas import (notna, Series, Index, Float64Index,
from pandas import (isna, notna, Series, Index, Float64Index,
Int64Index, RangeIndex)

import pandas.util.testing as tm
Expand Down Expand Up @@ -994,3 +994,22 @@ def test_append(self):
# Append single item rather than list
result2 = indices[0].append(indices[1])
tm.assert_index_equal(result2, expected, exact=True)

@pytest.mark.parametrize('start,stop,step',
[(0, 400, 3), (500, 0, -6), (-10**6, 10**6, 4),
(10**6, -10**6, -4), (0, 10, 20)])
def test_max_min(self, start, stop, step):
# GH17607
idx = RangeIndex(start, stop, step)
expected = idx._int64index.max()
result = idx.max()
assert result == expected

expected = idx._int64index.min()
result = idx.min()
assert result == expected

# empty
idx = RangeIndex(start, stop, -step)
assert isna(idx.max())
assert isna(idx.min())

0 comments on commit 26681db

Please sign in to comment.