From ffe49199d5f319a94f7c08ccd3f59457d26b4999 Mon Sep 17 00:00:00 2001 From: jreback Date: Tue, 1 Oct 2013 22:21:44 -0400 Subject: [PATCH] BUG: fixed a bug in multi-level indexing with a Timestamp partial indexer (GH4294) --- doc/source/release.rst | 1 + pandas/core/index.py | 28 +++++++++++++++++++------ pandas/index.pyx | 2 +- pandas/tseries/tests/test_timeseries.py | 18 ++++++++++++++++ 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/doc/source/release.rst b/doc/source/release.rst index 058ea165120a6..4e5178d8a554a 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -523,6 +523,7 @@ Bug Fixes and other reshaping issues. - Bug in setting with ``ix/loc`` and a mixed int/string index (:issue:`4544`) - Make sure series-series boolean comparions are label based (:issue:`4947`) + - Bug in multi-level indexing with a Timestamp partial indexer (:issue:`4294`) pandas 0.12.0 ------------- diff --git a/pandas/core/index.py b/pandas/core/index.py index f6a88f4164191..d6e74e16c8dae 100644 --- a/pandas/core/index.py +++ b/pandas/core/index.py @@ -1,4 +1,5 @@ # pylint: disable=E1101,E1103,W0232 +import datetime from functools import partial from pandas.compat import range, zip, lrange, lzip, u from pandas import compat @@ -2224,16 +2225,20 @@ def get_value(self, series, key): # Label-based s = _values_from_object(series) k = _values_from_object(key) + + def _try_mi(k): + # TODO: what if a level contains tuples?? + loc = self.get_loc(k) + new_values = series.values[loc] + new_index = self[loc] + new_index = _maybe_droplevels(new_index, k) + return Series(new_values, index=new_index, name=series.name) + try: return self._engine.get_value(s, k) except KeyError as e1: try: - # TODO: what if a level contains tuples?? - loc = self.get_loc(key) - new_values = series.values[loc] - new_index = self[loc] - new_index = _maybe_droplevels(new_index, key) - return Series(new_values, index=new_index, name=series.name) + return _try_mi(key) except KeyError: pass @@ -2250,6 +2255,16 @@ def get_value(self, series, key): except Exception: # pragma: no cover raise e1 except TypeError: + + # a Timestamp will raise a TypeError in a multi-index + # rather than a KeyError, try it here + if isinstance(key, (datetime.datetime,np.datetime64)) or ( + compat.PY3 and isinstance(key, compat.string_types)): + try: + return _try_mi(Timestamp(key)) + except: + pass + raise InvalidIndexError(key) def get_level_values(self, level): @@ -2779,6 +2794,7 @@ def reindex(self, target, method=None, level=None, limit=None, if level is not None: if method is not None: raise TypeError('Fill method not supported if level passed') + target = _ensure_index(target) target, indexer, _ = self._join_level(target, level, how='right', return_indexers=True) else: diff --git a/pandas/index.pyx b/pandas/index.pyx index 53c96b1c55605..8aa4f69a1ec8e 100644 --- a/pandas/index.pyx +++ b/pandas/index.pyx @@ -408,7 +408,7 @@ cdef class Float64Engine(IndexEngine): limit=limit) -cdef Py_ssize_t _bin_search(ndarray values, object val): +cdef Py_ssize_t _bin_search(ndarray values, object val) except -1: cdef: Py_ssize_t mid, lo = 0, hi = len(values) - 1 object pval diff --git a/pandas/tseries/tests/test_timeseries.py b/pandas/tseries/tests/test_timeseries.py index e4504420bacc2..f3598dd2d210b 100644 --- a/pandas/tseries/tests/test_timeseries.py +++ b/pandas/tseries/tests/test_timeseries.py @@ -2751,6 +2751,24 @@ def f(): df_multi.loc[('2013-06-19', 'ACCT1', 'ABC')] self.assertRaises(KeyError, f) + # GH 4294 + # partial slice on a series mi + s = pd.DataFrame(randn(1000, 1000), index=pd.date_range('2000-1-1', periods=1000)).stack() + + s2 = s[:-1].copy() + expected = s2['2000-1-4'] + result = s2[pd.Timestamp('2000-1-4')] + assert_series_equal(result, expected) + + result = s[pd.Timestamp('2000-1-4')] + expected = s['2000-1-4'] + assert_series_equal(result, expected) + + df2 = pd.DataFrame(s) + expected = df2.ix['2000-1-4'] + result = df2.ix[pd.Timestamp('2000-1-4')] + assert_frame_equal(result, expected) + def test_date_range_normalize(self): snap = datetime.today() n = 50