Skip to content

Commit

Permalink
PERF: don't call RangeIndex._data unneccesary
Browse files Browse the repository at this point in the history
  • Loading branch information
topper-123 committed May 29, 2019
1 parent d2beaf3 commit 3e29889
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 0 deletions.
6 changes: 6 additions & 0 deletions asv_bench/benchmarks/index_object.py
Expand Up @@ -95,6 +95,12 @@ def time_min(self):
def time_min_trivial(self):
self.idx_inc.min()

def time_get_loc_inc(self):
self.idx_inc.get_loc(900000)

def time_get_loc_dec(self):
self.idx_dec.get_loc(900000)


class IndexAppend:

Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.25.0.rst
Expand Up @@ -326,6 +326,7 @@ Performance Improvements
- Improved performance of :meth:`Series.searchsorted`. The speedup is especially large when the dtype is
int8/int16/int32 and the searched key is within the integer bounds for the dtype (:issue:`22034`)
- Improved performance of :meth:`pandas.core.groupby.GroupBy.quantile` (:issue:`20405`)
- Improved performance when slicing :class:`RangeIndex` (:issue:`xxxxx`)
- Improved performance of :meth:`read_csv` by faster tokenizing and faster parsing of small float numbers (:issue:`25784`)
- Improved performance of :meth:`read_csv` by faster parsing of N/A and boolean values (:issue:`25804`)
- Improved performance of :meth:`IntervalIndex.is_monotonic`, :meth:`IntervalIndex.is_monotonic_increasing` and :meth:`IntervalIndex.is_monotonic_decreasing` by removing conversion to :class:`MultiIndex` (:issue:`24813`)
Expand Down
18 changes: 18 additions & 0 deletions pandas/core/indexes/range.py
Expand Up @@ -21,6 +21,7 @@
import pandas.core.indexes.base as ibase
from pandas.core.indexes.base import Index, _index_shared_docs
from pandas.core.indexes.numeric import Int64Index
from pandas.io.formats.printing import pprint_thing


class RangeIndex(Int64Index):
Expand Down Expand Up @@ -64,6 +65,8 @@ class RangeIndex(Int64Index):
_typ = 'rangeindex'
_engine_type = libindex.Int64Engine

# check whether self._data has benn called
_has_called_data = False # type: bool
# --------------------------------------------------------------------
# Constructors

Expand Down Expand Up @@ -164,6 +167,8 @@ def _simple_new(cls, start, stop=None, step=None, name=None,
for k, v in kwargs.items():
setattr(result, k, v)

result._range = range(result._start, result._stop, result._step)

result._reset_identity()
return result

Expand All @@ -182,6 +187,7 @@ def _constructor(self):

@cache_readonly
def _data(self):
self._has_called_data = True
return np.arange(self._start, self._stop, self._step, dtype=np.int64)

@cache_readonly
Expand Down Expand Up @@ -215,6 +221,9 @@ def _format_data(self, name=None):
# we are formatting thru the attributes
return None

def _format_with_header(self, header, na_rep='NaN', **kwargs):
return header + [pprint_thing(x) for x in self._range]

# --------------------------------------------------------------------
@property
def start(self):
Expand Down Expand Up @@ -296,6 +305,15 @@ def is_monotonic_decreasing(self):
def has_duplicates(self):
return False

@Appender(_index_shared_docs['get_loc'])
def get_loc(self, key, method=None, tolerance=None):
if method is None and tolerance is None:
try:
return self._range.index(key)
except ValueError:
raise KeyError(key)
return super().__get_loc(key, method=method, tolerance=tolerance)

def tolist(self):
return list(range(self._start, self._stop, self._step))

Expand Down
30 changes: 30 additions & 0 deletions pandas/tests/indexes/test_range.py
Expand Up @@ -241,6 +241,36 @@ def test_view(self):
def test_dtype(self):
assert self.index.dtype == np.int64

def test_has_called_data(self):
# Calling RangeIndex._data caches a array of the same length.
# This tests whether RangeIndex._data has been called by doing methods
idx = RangeIndex(0, 100, 10)
assert idx._has_called_data is False

repr(idx)
assert idx._has_called_data is False

str(idx)
assert idx._has_called_data is False

idx.get_loc(20)
assert idx._has_called_data is False

df = pd.DataFrame({'a': range(10)}, index=idx)

df.loc[50]
assert idx._has_called_data is False

with pytest.raises(KeyError):
df.loc[51]
assert idx._has_called_data is False

df.loc[10:50]
assert idx._has_called_data is False

df.iloc[5:10]
assert idx._has_called_data is False

def test_is_monotonic(self):
assert self.index.is_monotonic is True
assert self.index.is_monotonic_increasing is True
Expand Down

0 comments on commit 3e29889

Please sign in to comment.