diff --git a/asv_bench/benchmarks/index_object.py b/asv_bench/benchmarks/index_object.py index 3fb53ce9b3c98..454d9ccdda102 100644 --- a/asv_bench/benchmarks/index_object.py +++ b/asv_bench/benchmarks/index_object.py @@ -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() diff --git a/doc/source/api.rst b/doc/source/api.rst index 6b3e6bedcb24b..96c7f68f57aaa 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -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 diff --git a/doc/source/whatsnew/v0.21.0.txt b/doc/source/whatsnew/v0.21.0.txt index 8bc9834ebef09..6157d73a76b31 100644 --- a/doc/source/whatsnew/v0.21.0.txt +++ b/doc/source/whatsnew/v0.21.0.txt @@ -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: diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index b759abaed4e56..16523257c2f77 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -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 diff --git a/pandas/tests/indexes/test_range.py b/pandas/tests/indexes/test_range.py index d206c36ee51c9..8dc5a40ced4bf 100644 --- a/pandas/tests/indexes/test_range.py +++ b/pandas/tests/indexes/test_range.py @@ -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 @@ -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())