Skip to content

Commit 172c7ab

Browse files
committed
ENH: RangeIndex, test suite passes now
1 parent ef878f0 commit 172c7ab

File tree

6 files changed

+146
-30
lines changed

6 files changed

+146
-30
lines changed

pandas/core/frame.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2432,7 +2432,7 @@ def set_index(self, keys, drop=True, append=False, inplace=False,
24322432
for i in range(self.index.nlevels):
24332433
arrays.append(self.index.get_level_values(i))
24342434
else:
2435-
arrays.append(np.asarray(self.index))
2435+
arrays.append(self.index.values)
24362436

24372437
to_remove = []
24382438
for col in keys:

pandas/core/index.py

Lines changed: 107 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
__all__ = ['Index']
1818

1919

20-
def _indexOp(opname):
20+
def _index_comp(opname):
2121
"""
2222
Wrapper function for index comparison operations, to avoid
2323
code duplication.
@@ -501,12 +501,12 @@ def __add__(self, other):
501501
else:
502502
return Index(self.view(np.ndarray) + other)
503503

504-
__eq__ = _indexOp('__eq__')
505-
__ne__ = _indexOp('__ne__')
506-
__lt__ = _indexOp('__lt__')
507-
__gt__ = _indexOp('__gt__')
508-
__le__ = _indexOp('__le__')
509-
__ge__ = _indexOp('__ge__')
504+
__eq__ = _index_comp('__eq__')
505+
__ne__ = _index_comp('__ne__')
506+
__lt__ = _index_comp('__lt__')
507+
__gt__ = _index_comp('__gt__')
508+
__le__ = _index_comp('__le__')
509+
__ge__ = _index_comp('__ge__')
510510

511511
def __sub__(self, other):
512512
return self.diff(other)
@@ -1071,6 +1071,7 @@ def slice_locs(self, start=None, end=None):
10711071
-----
10721072
This function assumes that the data is sorted, so use at your own peril
10731073
"""
1074+
values = None
10741075
if start is None:
10751076
beg_slice = 0
10761077
else:
@@ -1095,6 +1096,9 @@ def slice_locs(self, start=None, end=None):
10951096
end_slice += 1
10961097
except KeyError:
10971098
if self.is_monotonic:
1099+
if values is None:
1100+
values = self.values
1101+
10981102
end_slice = self.searchsorted(end, side='right')
10991103
else:
11001104
raise
@@ -1236,6 +1240,28 @@ def _wrap_joined_index(self, joined, other):
12361240
return Int64Index(joined, name=name)
12371241

12381242

1243+
def _range_comp(opname):
1244+
"""
1245+
Wrapper function for index comparison operations, to avoid
1246+
code duplication.
1247+
"""
1248+
def wrapper(self, other):
1249+
func = getattr(self.values, opname)
1250+
result = func(other)
1251+
return result
1252+
return wrapper
1253+
1254+
def _range_arith(opname):
1255+
"""
1256+
Wrapper function for index comparison operations, to avoid
1257+
code duplication.
1258+
"""
1259+
def wrapper(self, other):
1260+
func = getattr(self._to_dense(), opname)
1261+
return func(other)
1262+
return wrapper
1263+
1264+
12391265
class RangeIndex(Int64Index):
12401266
"""
12411267
Compactly represents regular integer range (instead of generating a full
@@ -1269,6 +1295,30 @@ def __array_finalize__(self, obj):
12691295
self.step = getattr(obj, 'step', None)
12701296
self.name = getattr(obj, 'name', None)
12711297

1298+
__eq__ = _range_comp('__eq__')
1299+
__ne__ = _range_comp('__ne__')
1300+
__lt__ = _range_comp('__lt__')
1301+
__gt__ = _range_comp('__gt__')
1302+
__le__ = _range_comp('__le__')
1303+
__ge__ = _range_comp('__ge__')
1304+
1305+
__add__ = _range_arith('__add__')
1306+
__sub__ = _range_arith('__sub__')
1307+
__mul__ = _range_arith('__mul__')
1308+
__truediv__ = _range_arith('__truediv__')
1309+
__floordiv__ = _range_arith('__floordiv__')
1310+
__radd__ = _range_arith('__radd__')
1311+
__rsub__ = _range_arith('__rsub__')
1312+
__rmul__ = _range_arith('__rmul__')
1313+
__rtruediv__ = _range_arith('__rtruediv__')
1314+
__rfloordiv__ = _range_arith('__rfloordiv__')
1315+
1316+
# Python 2 division operators
1317+
if not py3compat.PY3:
1318+
__div__ = _range_arith('__div__')
1319+
__rdiv__ = _range_arith('__rdiv__')
1320+
__idiv__ = __div__
1321+
12721322
@property
12731323
def values(self):
12741324
return np.arange(self.start, self.stop, self.step)
@@ -1329,10 +1379,10 @@ def __getitem__(self, key):
13291379
raise TypeError('Unhandled getitem type: %s' % type(key))
13301380

13311381
def insert(self, loc, item):
1332-
return Int64Index(self.values, name=self.name).insert(loc, item)
1382+
return self._to_dense().insert(loc, item)
13331383

13341384
def delete(self, loc):
1335-
return Int64Index(self.values, name=self.name).delete(loc)
1385+
return self._to_dense().delete(loc)
13361386

13371387
def take(self, key):
13381388
# XXX
@@ -1352,12 +1402,59 @@ def equals(self, other):
13521402
"""
13531403
if not isinstance(other, RangeIndex):
13541404
if isinstance(other, Index):
1355-
other = other.values
1405+
return other.equals(self._to_dense())
13561406
return np.array_equal(self.values, other)
13571407

13581408
return (self.start == other.start and self.stop == other.stop
13591409
and self.step == other.step)
13601410

1411+
def searchsorted(self, key, side='left'):
1412+
return self.values.searchsorted(key, side=side)
1413+
1414+
def _to_dense(self):
1415+
return Int64Index(self.values, name=self.name)
1416+
1417+
def union(self, other):
1418+
"""
1419+
Specialized Index.union for RangeIndex
1420+
"""
1421+
if not isinstance(other, RangeIndex):
1422+
return self._to_dense().union(other)
1423+
1424+
if self.step == other.step:
1425+
this = self
1426+
if self.start > other.start:
1427+
this, other = other, this
1428+
1429+
if this.stop > other.start:
1430+
return RangeIndex(this.start, max(other.stop, this.stop),
1431+
self.step)
1432+
else:
1433+
return self._to_dense().union(other._to_dense())
1434+
else:
1435+
return self._to_dense().union(other._to_dense())
1436+
1437+
def intersection(self, other):
1438+
"""
1439+
Specialized Index.intersection for RangeIndex
1440+
"""
1441+
if not isinstance(other, RangeIndex):
1442+
return self._to_dense().intersection(other)
1443+
1444+
if self.step == other.step:
1445+
this = self
1446+
if self.start > other.start:
1447+
this, other = other, this
1448+
1449+
if other.stop <= this.stop:
1450+
return RangeIndex(other.start, min(this.stop, other.stop),
1451+
self.step)
1452+
else:
1453+
return self._to_dense().intersection(other._to_dense())
1454+
else:
1455+
return self._to_dense().intersection(other._to_dense())
1456+
1457+
13611458
@property
13621459
def _constructor(self):
13631460
return RangeIndex

pandas/core/series.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
import numpy.ma as ma
1616

1717
from pandas.core.common import (isnull, notnull, _is_bool_indexer,
18-
_default_index, _maybe_upcast,
18+
_maybe_upcast,
1919
_asarray_tuplesafe, is_integer_dtype)
2020
from pandas.core.index import (Index, MultiIndex, InvalidIndexError,
21-
_ensure_index, _handle_legacy_indexes)
21+
_ensure_index, _handle_legacy_indexes,
22+
RangeIndex)
2223
from pandas.core.indexing import _SeriesIndexer
2324
from pandas.tseries.index import DatetimeIndex
2425
from pandas.tseries.period import PeriodIndex, Period
@@ -340,7 +341,7 @@ def __new__(cls, data=None, index=None, dtype=None, name=None,
340341
return subarr
341342

342343
if index is None:
343-
index = _default_index(len(subarr))
344+
index = RangeIndex(len(subarr))
344345

345346
# Change the class of the array to be the subclass type.
346347
if index.is_all_dates:
@@ -363,7 +364,9 @@ def from_array(cls, arr, index=None, name=None, copy=False):
363364
arr = arr.copy()
364365

365366
klass = Series
366-
if index.is_all_dates:
367+
if index is None:
368+
index = RangeIndex(len(arr))
369+
elif index.is_all_dates:
367370
if not isinstance(index, (DatetimeIndex, PeriodIndex)):
368371
index = DatetimeIndex(index)
369372
klass = TimeSeries
@@ -1153,7 +1156,7 @@ def max(self, axis=None, out=None, skipna=True, level=None):
11531156

11541157
@Substitution(name='standard deviation', shortname='stdev',
11551158
na_action=_doc_exclude_na, extras='')
1156-
@Appender(_stat_doc +
1159+
@Appender(_stat_doc +
11571160
"""
11581161
Normalized by N-1 (unbiased estimator).
11591162
""")
@@ -1166,7 +1169,7 @@ def std(self, axis=None, dtype=None, out=None, ddof=1, skipna=True,
11661169

11671170
@Substitution(name='variance', shortname='var',
11681171
na_action=_doc_exclude_na, extras='')
1169-
@Appender(_stat_doc +
1172+
@Appender(_stat_doc +
11701173
"""
11711174
Normalized by N-1 (unbiased estimator).
11721175
""")

pandas/tests/test_frame.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,14 +1841,14 @@ def test_constructor_ndarray(self):
18411841

18421842
# automatic labeling
18431843
frame = DataFrame(mat)
1844-
self.assert_(np.array_equal(frame.index, range(2)))
1845-
self.assert_(np.array_equal(frame.columns, range(3)))
1844+
self.assert_(frame.index.equals(range(2)))
1845+
self.assert_(frame.columns.equals(range(3)))
18461846

18471847
frame = DataFrame(mat, index=[1, 2])
1848-
self.assert_(np.array_equal(frame.columns, range(3)))
1848+
self.assert_(frame.columns.equals(range(3)))
18491849

18501850
frame = DataFrame(mat, columns=['A', 'B', 'C'])
1851-
self.assert_(np.array_equal(frame.index, range(2)))
1851+
self.assert_(frame.index.equals(range(2)))
18521852

18531853
# 0-length axis
18541854
frame = DataFrame(np.empty((0, 3)))
@@ -1899,14 +1899,14 @@ def test_constructor_maskedarray(self):
18991899

19001900
# automatic labeling
19011901
frame = DataFrame(mat)
1902-
self.assert_(np.array_equal(frame.index, range(2)))
1903-
self.assert_(np.array_equal(frame.columns, range(3)))
1902+
self.assert_(frame.index.equals(range(2)))
1903+
self.assert_(frame.columns.equals(range(3)))
19041904

19051905
frame = DataFrame(mat, index=[1, 2])
1906-
self.assert_(np.array_equal(frame.columns, range(3)))
1906+
self.assert_(frame.columns.equals(range(3)))
19071907

19081908
frame = DataFrame(mat, columns=['A', 'B', 'C'])
1909-
self.assert_(np.array_equal(frame.index, range(2)))
1909+
self.assert_(frame.index.equals(range(2)))
19101910

19111911
# 0-length axis
19121912
frame = DataFrame(ma.masked_all((0, 3)))
@@ -6677,8 +6677,7 @@ def test_reset_index(self):
66776677
deleveled = self.frame.reset_index()
66786678
self.assert_(np.array_equal(deleveled['index'],
66796679
self.frame.index.values))
6680-
self.assert_(np.array_equal(deleveled.index,
6681-
np.arange(len(deleveled))))
6680+
self.assert_(deleveled.index.equals(np.arange(len(deleveled))))
66826681

66836682
# preserve column names
66846683
self.frame.columns.name = 'columns'

pandas/tests/test_index.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,10 @@ def test_slice(self):
962962
index = RangeIndex(0, 1)
963963
self.assertTrue(index[:0].equals(RangeIndex(0, 0)))
964964

965+
def test_slice_locs(self):
966+
index = RangeIndex(0, 2)
967+
self.assertEqual(index.slice_locs(0, 3), (0, 2))
968+
965969
def test_insert(self):
966970
index = RangeIndex(5, name='bar')
967971

@@ -978,10 +982,23 @@ def test_delete(self):
978982
self.assertEqual(result.name, 'bar')
979983

980984
def test_union(self):
981-
pass
985+
result = RangeIndex(4).union(RangeIndex(3))
986+
self.assertTrue(result.equals(RangeIndex(4)))
987+
988+
result = RangeIndex(3).union(RangeIndex(4, 7))
989+
exp = Index([0, 1, 2, 4, 5, 6])
990+
self.assertTrue(result.equals(exp))
982991

983992
def test_intersection(self):
984-
pass
993+
result = RangeIndex(4).intersection(RangeIndex(1, 3))
994+
self.assertTrue(result.equals(RangeIndex(1, 3)))
995+
996+
def test_array_arithmetic(self):
997+
index = RangeIndex(0, 11)
998+
999+
result = index // 5
1000+
expected = index._to_dense() // 5
1001+
self.assertTrue(result.equals(expected))
9851002

9861003
def test_pickle(self):
9871004
pass

pandas/tools/tests/test_merge.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ def test_join_sort(self):
560560

561561
# smoke test
562562
joined = left.join(right, on='key', sort=False)
563-
self.assert_(np.array_equal(joined.index, range(4)))
563+
self.assert_(joined.index.equals(range(4)))
564564

565565
def test_intelligently_handle_join_key(self):
566566
# #733, be a bit more 1337 about not returning unconsolidated DataFrame

0 commit comments

Comments
 (0)