From 8e17545ba5230f3551b7dc86e652ff7f2ad4bf32 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 29 Jan 2020 14:48:52 -0800 Subject: [PATCH 1/3] REF: make PeriodIndex.get_loc conform to DTI/TDI.get_loc --- pandas/_libs/index.pyx | 23 +++++++++++++++++++++++ pandas/core/indexes/period.py | 19 ++++--------------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/pandas/_libs/index.pyx b/pandas/_libs/index.pyx index 2dfc14378baf6..1e2483dcddac4 100644 --- a/pandas/_libs/index.pyx +++ b/pandas/_libs/index.pyx @@ -17,6 +17,7 @@ cnp.import_array() cimport pandas._libs.util as util +from pandas._libs.tslibs import Period from pandas._libs.tslibs.nattype cimport c_NaT as NaT from pandas._libs.tslibs.c_timestamp cimport _Timestamp @@ -510,6 +511,28 @@ cdef class TimedeltaEngine(DatetimeEngine): cdef class PeriodEngine(Int64Engine): + cdef int64_t _unbox_scalar(self, scalar) except? -1: + if scalar is NaT: + return scalar.value + if isinstance(scalar, Period): + # NB: we assume that we have the correct freq here. + # TODO: potential optimize by checking for _Period? + return scalar.ordinal + raise TypeError(scalar) + + cpdef get_loc(self, object val): + # NB: the caller is responsible for ensuring that we are called + # with either a Period or NaT + cdef: + int64_t conv + + try: + conv = self._unbox_scalar(val) + except TypeError: + raise KeyError(val) + + return Int64Engine.get_loc(self, conv) + cdef _get_index_values(self): return super(PeriodEngine, self).vgetter().view("i8") diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 1e18c16d02784..fe92e60a283fc 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -18,7 +18,6 @@ is_float, is_integer, is_integer_dtype, - is_list_like, is_object_dtype, is_scalar, pandas_dtype, @@ -547,6 +546,7 @@ def get_loc(self, key, method=None, tolerance=None): TypeError If key is listlike or otherwise not hashable. """ + orig_key = key if not is_scalar(key): raise InvalidIndexError(key) @@ -588,23 +588,12 @@ def get_loc(self, key, method=None, tolerance=None): key = Period(key, freq=self.freq) except ValueError: # we cannot construct the Period - # as we have an invalid type - if is_list_like(key): - raise TypeError(f"'{key}' is an invalid key") - raise KeyError(key) + raise KeyError(orig_key) - ordinal = key.ordinal if key is not NaT else key.value try: - return self._engine.get_loc(ordinal) + return Index.get_loc(self, key, method, tolerance) except KeyError: - - try: - if tolerance is not None: - tolerance = self._convert_tolerance(tolerance, np.asarray(key)) - return self._int64index.get_loc(ordinal, method, tolerance) - - except KeyError: - raise KeyError(key) + raise KeyError(orig_key) def _maybe_cast_slice_bound(self, label, side, kind): """ From 71ce7d05fd027cb0668711b26160d95f6e73996d Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 2 Feb 2020 16:24:40 -0800 Subject: [PATCH 2/3] rebase fixup --- pandas/core/series.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index bfe9969daaa8e..040fcf392733b 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -23,6 +23,7 @@ from pandas._config import get_option from pandas._libs import lib, properties, reshape, tslibs +from pandas._libs.index import validate_numeric_casting from pandas._typing import Label from pandas.compat.numpy import function as nv from pandas.util._decorators import Appender, Substitution @@ -1022,7 +1023,7 @@ def __setitem__(self, key, value): def _set_with_engine(self, key, value): # fails with AttributeError for IntervalIndex loc = self.index._engine.get_loc(key) - libindex.validate_numeric_casting(self.dtype, value) + validate_numeric_casting(self.dtype, value) self._values[loc] = value def _set_with(self, key, value): @@ -1105,7 +1106,7 @@ def _set_value(self, label, value, takeable: bool = False): self._values[label] = value else: loc = self.index.get_loc(label) - libindex.validate_numeric_casting(self.dtype, value) + validate_numeric_casting(self.dtype, value) self._values[loc] = value except KeyError: From 8bced7152f75ed77a822bda076bc3fc1cc248ca7 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 2 Feb 2020 19:14:18 -0800 Subject: [PATCH 3/3] Fix convert_tolerance --- pandas/core/indexes/period.py | 12 +++++------- pandas/tests/indexing/test_indexing.py | 1 - 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index dff1c052a7d65..1ec4a9dbe27ad 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -5,7 +5,7 @@ import numpy as np from pandas._libs import index as libindex -from pandas._libs.tslibs import NaT, frequencies as libfrequencies, resolution +from pandas._libs.tslibs import frequencies as libfrequencies, resolution from pandas._libs.tslibs.parsing import parse_time_string from pandas._libs.tslibs.period import Period from pandas.util._decorators import Appender, cache_readonly @@ -468,6 +468,10 @@ def get_indexer(self, target, method=None, limit=None, tolerance=None): if tolerance is not None: tolerance = self._convert_tolerance(tolerance, target) + if self_index is not self: + # convert tolerance to i8 + tolerance = self._maybe_convert_timedelta(tolerance) + return Index.get_indexer(self_index, target, method, limit, tolerance) @Appender(_index_shared_docs["get_indexer_non_unique"] % _index_doc_kwargs) @@ -639,12 +643,6 @@ def _get_string_slice(self, key: str, use_lhs: bool = True, use_rhs: bool = True # try to find a the dates return (lhs_mask & rhs_mask).nonzero()[0] - def _convert_tolerance(self, tolerance, target): - tolerance = DatetimeIndexOpsMixin._convert_tolerance(self, tolerance, target) - if target.size != tolerance.size and tolerance.size > 1: - raise ValueError("list-like tolerance size must match target index size") - return self._maybe_convert_timedelta(tolerance) - def insert(self, loc, item): if not isinstance(item, Period) or self.freq != item.freq: return self.astype(object).insert(loc, item) diff --git a/pandas/tests/indexing/test_indexing.py b/pandas/tests/indexing/test_indexing.py index ae32274c02dcd..08596f0661d2c 100644 --- a/pandas/tests/indexing/test_indexing.py +++ b/pandas/tests/indexing/test_indexing.py @@ -153,7 +153,6 @@ def test_setitem_ndarray_3d(self, index, obj, idxr, idxr_id): "floating", "string", "datetime64", - "period", "timedelta64", "boolean", "categorical",