Skip to content

Commit f3ca67d

Browse files
committed
ENH: raise AmbiguousIndexError when location not found in iteger-indexed Series. Start of #328 API changes
1 parent 0bdbe8d commit f3ca67d

File tree

9 files changed

+85
-32
lines changed

9 files changed

+85
-32
lines changed

pandas/core/common.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
class PandasError(Exception):
2828
pass
2929

30+
class AmbiguousIndexError(PandasError, KeyError):
31+
pass
32+
3033
def isnull(obj):
3134
'''
3235
Replacement for numpy.isnan / -numpy.isfinite which is suitable

pandas/core/frame.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,7 @@ def _sanitize_column(self, value):
13681368
else:
13691369
value = np.repeat(value, len(self.index))
13701370

1371-
return value
1371+
return np.asarray(value)
13721372

13731373
def pop(self, item):
13741374
"""
@@ -1729,7 +1729,9 @@ def reset_index(self):
17291729

17301730
# to ndarray and maybe infer different dtype
17311731
level_values = lev.values
1732-
level_values = lib.maybe_convert_objects(level_values)
1732+
if level_values.dtype == np.object_:
1733+
level_values = lib.maybe_convert_objects(level_values)
1734+
17331735
new_obj.insert(0, col_name, level_values.take(lab))
17341736
else:
17351737
name = self.index.name

pandas/core/index.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ def __new__(cls, data, dtype=None, copy=False, name=None):
6666
# other iterable of some kind
6767
subarr = _asarray_tuplesafe(data, dtype=object)
6868

69+
if lib.is_integer_array(subarr) and dtype is None:
70+
return Int64Index(subarr.astype('i8'), name=name)
71+
6972
subarr = subarr.view(cls)
7073
subarr.name = name
7174
return subarr
@@ -74,7 +77,8 @@ def __array_finalize__(self, obj):
7477
self.name = getattr(obj, 'name', None)
7578

7679
def astype(self, dtype):
77-
return Index(self.values.astype(dtype), name=self.name)
80+
return Index(self.values.astype(dtype), name=self.name,
81+
dtype=dtype)
7882

7983
@property
8084
def dtype(self):
@@ -857,13 +861,14 @@ def __new__(cls, data, dtype=None, copy=False, name=None):
857861
subarr.name = name
858862
return subarr
859863

864+
@property
865+
def inferred_type(self):
866+
return 'integer'
867+
860868
@property
861869
def _constructor(self):
862870
return Int64Index
863871

864-
def astype(self, dtype):
865-
return Index(self.values.astype(dtype))
866-
867872
@property
868873
def dtype(self):
869874
return np.dtype('int64')

pandas/core/series.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
from pandas.core.common import (isnull, notnull, _is_bool_indexer,
1717
_default_index, _maybe_upcast,
18-
_asarray_tuplesafe)
18+
_asarray_tuplesafe,
19+
AmbiguousIndexError)
1920
from pandas.core.daterange import DateRange
2021
from pandas.core.index import Index, MultiIndex, _ensure_index
2122
from pandas.core.indexing import _SeriesIndexer, _maybe_droplevels
@@ -137,6 +138,8 @@ class Series(np.ndarray, generic.PandasObject):
137138

138139
_AXIS_NAMES = dict((v, k) for k, v in _AXIS_NUMBERS.iteritems())
139140

141+
__slots__ = ['_index', 'name']
142+
140143
def __new__(cls, data=None, index=None, dtype=None, name=None,
141144
copy=False):
142145
if data is None:
@@ -208,20 +211,22 @@ def __hash__(self):
208211
raise TypeError('unhashable type')
209212

210213
_index = None
211-
def _get_index(self):
212-
return self._index
214+
index = lib.SeriesIndex()
215+
216+
# def _get_index(self):
217+
# return self._index
213218

214-
def _set_index(self, index):
215-
if not isinstance(index, _INDEX_TYPES):
216-
raise TypeError("Expected index to be in %s; was %s."
217-
% (_INDEX_TYPES, type(index)))
219+
# def _set_index(self, index):
220+
# if not isinstance(index, _INDEX_TYPES):
221+
# raise TypeError("Expected index to be in %s; was %s."
222+
# % (_INDEX_TYPES, type(index)))
218223

219-
if len(self) != len(index):
220-
raise AssertionError('Lengths of index and values did not match!')
224+
# if len(self) != len(index):
225+
# raise AssertionError('Lengths of index and values did not match!')
221226

222-
self._index = _ensure_index(index)
227+
# self._index = _ensure_index(index)
223228

224-
index = property(fget=_get_index, fset=_set_index)
229+
# index = property(fget=_get_index, fset=_set_index)
225230

226231
def __array_finalize__(self, obj):
227232
"""
@@ -264,22 +269,27 @@ def ix(self):
264269
return self._ix
265270

266271
def __getitem__(self, key):
272+
index = self.index
273+
267274
# Label-based
268275
try:
269-
return self.index._engine.get_value(self, key)
276+
return index._engine.get_value(self, key)
270277
except KeyError, e1:
271-
if isinstance(self.index, MultiIndex):
278+
if isinstance(index, MultiIndex):
272279
values = self.values
273280
try:
274-
loc = self.index.get_loc(key)
281+
loc = index.get_loc(key)
275282
# TODO: what if a level contains tuples??
276-
new_index = self.index[loc]
283+
new_index = index[loc]
277284
new_index = _maybe_droplevels(new_index, key)
278285
return Series(values[loc], index=new_index,
279286
name=self.name)
280287
except KeyError:
281288
pass
282289

290+
if index.inferred_type == 'integer':
291+
raise AmbiguousIndexError(key)
292+
283293
try:
284294
return _gin.get_value_at(self, key)
285295
except IndexError:

pandas/sparse/tests/test_sparse.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,10 @@ def _check_getitem(sp, dense):
286286
# j = np.float64(i)
287287
# assert_almost_equal(sp[j], dense[j])
288288

289+
# API change 1/6/2012
289290
# negative getitem works
290-
for i in xrange(len(dense)):
291-
assert_almost_equal(sp[-i], dense[-i])
291+
# for i in xrange(len(dense)):
292+
# assert_almost_equal(sp[-i], dense[-i])
292293

293294
_check_getitem(self.bseries, self.bseries.to_dense())
294295
_check_getitem(self.btseries, self.btseries.to_dense())
@@ -563,7 +564,8 @@ def test_valid(self):
563564
fill_value=0)
564565

565566
sp_valid = sp.valid()
566-
assert_almost_equal(sp_valid, sp.to_dense().valid())
567+
assert_almost_equal(sp_valid.values,
568+
sp.to_dense().valid().values)
567569
self.assert_(sp_valid.index.equals(sp.to_dense().valid().index))
568570
self.assertEquals(len(sp_valid.sp_values), 2)
569571

pandas/src/properties.pyx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,20 @@ cdef class AxisProperty(object):
4141

4242
def __set__(self, obj, value):
4343
obj._set_axis(self.axis, value)
44+
45+
cdef class SeriesIndex(object):
46+
cdef:
47+
Py_ssize_t axis
48+
object _check_type
49+
50+
def __init__(self):
51+
from pandas.core.index import _ensure_index
52+
self._check_type = _ensure_index
53+
54+
def __get__(self, obj, type):
55+
return obj._index
56+
57+
def __set__(self, obj, value):
58+
if len(obj) != len(value):
59+
raise AssertionError('Index length did not match values')
60+
obj._index = self._check_type(value)

pandas/tests/test_index.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,15 @@ def test_constructor_corner(self):
404404
arr = np.array([1, '2', 3, '4'], dtype=object)
405405
self.assertRaises(TypeError, Int64Index, arr)
406406

407+
def test_coerce_list(self):
408+
# coerce things
409+
arr = Index([1, 2, 3, 4])
410+
self.assert_(type(arr) == Int64Index)
411+
412+
# but not if explicit dtype passed
413+
arr = Index([1, 2, 3, 4], dtype=object)
414+
self.assert_(type(arr) == Index)
415+
407416
def test_dtype(self):
408417
self.assert_(self.index.dtype == np.int64)
409418

pandas/tests/test_series.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,11 @@ def test_fromDict(self):
262262
def test_setindex(self):
263263
# wrong type
264264
series = self.series.copy()
265-
self.assertRaises(TypeError, series._set_index, None)
265+
self.assertRaises(TypeError, setattr, series, 'index', None)
266266

267267
# wrong length
268268
series = self.series.copy()
269-
self.assertRaises(AssertionError, series._set_index,
269+
self.assertRaises(AssertionError, setattr, series, 'index',
270270
np.arange(len(series) - 1))
271271

272272
# works
@@ -400,6 +400,11 @@ def test_getitem_box_float64(self):
400400
value = self.ts[5]
401401
self.assert_(isinstance(value, np.float64))
402402

403+
def test_getitem_ambiguous_keyerror(self):
404+
s = Series(range(10), index=range(0, 20, 2))
405+
self.assertRaises(KeyError, s.__getitem__, 1)
406+
self.assertRaises(KeyError, s.ix.__getitem__, 1)
407+
403408
def test_slice(self):
404409
numSlice = self.series[10:20]
405410
numSliceEnd = self.series[-10:]

pandas/tests/test_tseries.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,31 @@ def test_merge_indexer(self):
2727
old = Index([1, 5, 10])
2828
new = Index(range(12))
2929

30-
filler = lib.merge_indexer_object(new, old.indexMap)
30+
filler = lib.merge_indexer_int64(new, old.indexMap)
3131

3232
expect_filler = [-1, 0, -1, -1, -1, 1, -1, -1, -1, -1, 2, -1]
3333
self.assert_(np.array_equal(filler, expect_filler))
3434

3535
# corner case
3636
old = Index([1, 4])
3737
new = Index(range(5, 10))
38-
filler = lib.merge_indexer_object(new, old.indexMap)
38+
filler = lib.merge_indexer_int64(new, old.indexMap)
3939
expect_filler = [-1, -1, -1, -1, -1]
4040
self.assert_(np.array_equal(filler, expect_filler))
4141

4242
def test_backfill(self):
4343
old = Index([1, 5, 10])
4444
new = Index(range(12))
4545

46-
filler = lib.backfill_object(old, new, old.indexMap, new.indexMap)
46+
filler = lib.backfill_int64(old, new, old.indexMap, new.indexMap)
4747

4848
expect_filler = [0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, -1]
4949
self.assert_(np.array_equal(filler, expect_filler))
5050

5151
# corner case
5252
old = Index([1, 4])
5353
new = Index(range(5, 10))
54-
filler = lib.backfill_object(old, new, old.indexMap, new.indexMap)
54+
filler = lib.backfill_int64(old, new, old.indexMap, new.indexMap)
5555

5656
expect_filler = [-1, -1, -1, -1, -1]
5757
self.assert_(np.array_equal(filler, expect_filler))
@@ -60,15 +60,15 @@ def test_pad(self):
6060
old = Index([1, 5, 10])
6161
new = Index(range(12))
6262

63-
filler = lib.pad_object(old, new, old.indexMap, new.indexMap)
63+
filler = lib.pad_int64(old, new, old.indexMap, new.indexMap)
6464

6565
expect_filler = [-1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2]
6666
self.assert_(np.array_equal(filler, expect_filler))
6767

6868
# corner case
6969
old = Index([5, 10])
7070
new = Index(range(5))
71-
filler = lib.pad_object(old, new, old.indexMap, new.indexMap)
71+
filler = lib.pad_int64(old, new, old.indexMap, new.indexMap)
7272
expect_filler = [-1, -1, -1, -1, -1]
7373
self.assert_(np.array_equal(filler, expect_filler))
7474

0 commit comments

Comments
 (0)