From 2783fdb99f432177b3ad9593a10d0626a88b4d95 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 9 Jan 2020 08:30:10 -0800 Subject: [PATCH 1/2] REF: share DTI/TDI.insert --- pandas/core/indexes/datetimelike.py | 58 ++++++++++++++++++++++++++++- pandas/core/indexes/datetimes.py | 57 +--------------------------- pandas/core/indexes/timedeltas.py | 57 +--------------------------- 3 files changed, 59 insertions(+), 113 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index c4dac9d1c4a11..9b03c9799a3a1 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -27,7 +27,7 @@ ) from pandas.core.dtypes.concat import concat_compat from pandas.core.dtypes.generic import ABCIndex, ABCIndexClass, ABCSeries -from pandas.core.dtypes.missing import isna +from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna from pandas.core import algorithms from pandas.core.accessor import PandasDelegate @@ -889,6 +889,62 @@ def _wrap_joined_index(self, joined, other): kwargs["tz"] = getattr(other, "tz", None) return self._simple_new(joined, name, **kwargs) + # -------------------------------------------------------------------- + # List-Like Methods + + def insert(self, loc, item): + """ + Make new Index inserting new item at location + + Parameters + ---------- + loc : int + item : object + if not either a Python datetime or a numpy integer-like, returned + Index dtype will be object rather than datetime. + + Returns + ------- + new_index : Index + """ + if isinstance(item, self._data._recognized_scalars): + item = self._data._scalar_type(item) + elif is_valid_nat_for_dtype(item, self.dtype): + # GH 18295 + item = self._na_value + elif is_scalar(item) and isna(item): + raise TypeError( + f"cannot insert {type(self).__name__} with incompatible label" + ) + + freq = None + if isinstance(item, self._data._scalar_type) or item is NaT: + self._data._check_compatible_with(item, setitem=True) + + # check freq can be preserved on edge cases + if self.size and self.freq is not None: + if item is NaT: + pass + elif (loc == 0 or loc == -len(self)) and item + self.freq == self[0]: + freq = self.freq + elif (loc == len(self)) and item - self.freq == self[-1]: + freq = self.freq + item = item.asm8 + + try: + new_i8s = np.concatenate( + (self[:loc].asi8, [item.view(np.int64)], self[loc:].asi8) + ) + return self._shallow_copy(new_i8s, freq=freq) + except (AttributeError, TypeError): + + # fall back to object index + if isinstance(item, str): + return self.astype(object).insert(loc, item) + raise TypeError( + f"cannot insert {type(self).__name__} with incompatible label" + ) + class DatetimelikeDelegateMixin(PandasDelegate): """ diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index e8ba2a8799dc7..eb25bdd658793 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -5,13 +5,12 @@ import numpy as np -from pandas._libs import NaT, Timestamp, index as libindex, lib, tslib as libts +from pandas._libs import Timestamp, index as libindex, lib, tslib as libts from pandas._libs.tslibs import ccalendar, fields, parsing, timezones from pandas.util._decorators import Appender, Substitution, cache_readonly from pandas.core.dtypes.common import _NS_DTYPE, is_float, is_integer, is_scalar from pandas.core.dtypes.dtypes import DatetimeTZDtype -from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna from pandas.core.accessor import delegate_names from pandas.core.arrays.datetimes import ( @@ -865,60 +864,6 @@ def inferred_type(self) -> str: # sure we can't have ambiguous indexing return "datetime64" - def insert(self, loc, item): - """ - Make new Index inserting new item at location - - Parameters - ---------- - loc : int - item : object - if not either a Python datetime or a numpy integer-like, returned - Index dtype will be object rather than datetime. - - Returns - ------- - new_index : Index - """ - if isinstance(item, self._data._recognized_scalars): - item = self._data._scalar_type(item) - elif is_valid_nat_for_dtype(item, self.dtype): - # GH 18295 - item = self._na_value - elif is_scalar(item) and isna(item): - # i.e. timedeltat64("NaT") - raise TypeError( - f"cannot insert {type(self).__name__} with incompatible label" - ) - - freq = None - if isinstance(item, self._data._scalar_type) or item is NaT: - self._data._check_compatible_with(item, setitem=True) - - # check freq can be preserved on edge cases - if self.size and self.freq is not None: - if item is NaT: - pass - elif (loc == 0 or loc == -len(self)) and item + self.freq == self[0]: - freq = self.freq - elif (loc == len(self)) and item - self.freq == self[-1]: - freq = self.freq - item = item.asm8 - - try: - new_i8s = np.concatenate( - (self[:loc].asi8, [item.view(np.int64)], self[loc:].asi8) - ) - return self._shallow_copy(new_i8s, freq=freq) - except (AttributeError, TypeError): - - # fall back to object index - if isinstance(item, str): - return self.astype(object).insert(loc, item) - raise TypeError( - f"cannot insert {type(self).__name__} with incompatible label" - ) - def indexer_at_time(self, time, asof=False): """ Return index locations of index values at particular time of day diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 1f3182bc83e1d..1ded13abb7cbc 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -16,7 +16,7 @@ is_timedelta64_ns_dtype, pandas_dtype, ) -from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna +from pandas.core.dtypes.missing import isna from pandas.core.accessor import delegate_names from pandas.core.arrays import datetimelike as dtl @@ -375,61 +375,6 @@ def is_type_compatible(self, typ) -> bool: def inferred_type(self) -> str: return "timedelta64" - def insert(self, loc, item): - """ - Make new Index inserting new item at location - - Parameters - ---------- - loc : int - item : object - If not either a Python datetime or a numpy integer-like, returned - Index dtype will be object rather than datetime. - - Returns - ------- - new_index : Index - """ - # try to convert if possible - if isinstance(item, self._data._recognized_scalars): - item = self._data._scalar_type(item) - elif is_valid_nat_for_dtype(item, self.dtype): - # GH 18295 - item = self._na_value - elif is_scalar(item) and isna(item): - # i.e. datetime64("NaT") - raise TypeError( - f"cannot insert {type(self).__name__} with incompatible label" - ) - - freq = None - if isinstance(item, self._data._scalar_type) or item is NaT: - self._data._check_compatible_with(item, setitem=True) - - # check freq can be preserved on edge cases - if self.size and self.freq is not None: - if item is NaT: - pass - elif (loc == 0 or loc == -len(self)) and item + self.freq == self[0]: - freq = self.freq - elif (loc == len(self)) and item - self.freq == self[-1]: - freq = self.freq - item = item.asm8 - - try: - new_i8s = np.concatenate( - (self[:loc].asi8, [item.view(np.int64)], self[loc:].asi8) - ) - return self._shallow_copy(new_i8s, freq=freq) - except (AttributeError, TypeError): - - # fall back to object index - if isinstance(item, str): - return self.astype(object).insert(loc, item) - raise TypeError( - f"cannot insert {type(self).__name__} with incompatible label" - ) - TimedeltaIndex._add_logical_methods_disabled() From 69c67a49cc536953cf41add5342134d3a4cf7c62 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 9 Jan 2020 08:36:52 -0800 Subject: [PATCH 2/2] REF: share searchsorted --- pandas/core/indexes/datetimelike.py | 25 +++++++++++++++++++++++++ pandas/core/indexes/datetimes.py | 27 +-------------------------- pandas/core/indexes/timedeltas.py | 27 +-------------------------- 3 files changed, 27 insertions(+), 52 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 9b03c9799a3a1..bb7aa84520019 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -945,6 +945,31 @@ def insert(self, loc, item): f"cannot insert {type(self).__name__} with incompatible label" ) + # -------------------------------------------------------------------- + # NDarray-like Methods + + def searchsorted(self, value, side="left", sorter=None): + if isinstance(value, (np.ndarray, Index)): + if not type(self._data)._is_recognized_dtype(value): + raise TypeError( + "searchsorted requires compatible dtype or scalar, " + f"not {type(value).__name__}" + ) + value = type(self._data)(value) + self._data._check_compatible_with(value) + + elif isinstance(value, self._data._recognized_scalars) or value is NaT: + self._data._check_compatible_with(value) + value = self._data._scalar_type(value) + + elif not isinstance(value, type(self._data)): + raise TypeError( + "searchsorted requires compatible dtype or scalar, " + f"not {type(value).__name__}" + ) + + return self._data.searchsorted(value, side=side, sorter=sorter) + class DatetimelikeDelegateMixin(PandasDelegate): """ diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index eb25bdd658793..db5ca78875e1b 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -7,7 +7,7 @@ from pandas._libs import Timestamp, index as libindex, lib, tslib as libts from pandas._libs.tslibs import ccalendar, fields, parsing, timezones -from pandas.util._decorators import Appender, Substitution, cache_readonly +from pandas.util._decorators import cache_readonly from pandas.core.dtypes.common import _NS_DTYPE, is_float, is_integer, is_scalar from pandas.core.dtypes.dtypes import DatetimeTZDtype @@ -18,7 +18,6 @@ tz_to_dtype, validate_tz_from_dtype, ) -from pandas.core.base import _shared_docs import pandas.core.common as com from pandas.core.indexes.base import Index, maybe_extract_name from pandas.core.indexes.datetimelike import ( @@ -831,30 +830,6 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): # -------------------------------------------------------------------- - @Substitution(klass="DatetimeIndex") - @Appender(_shared_docs["searchsorted"]) - def searchsorted(self, value, side="left", sorter=None): - if isinstance(value, (np.ndarray, Index)): - if not type(self._data)._is_recognized_dtype(value): - raise TypeError( - "searchsorted requires compatible dtype or scalar, " - f"not {type(value).__name__}" - ) - value = type(self._data)(value) - self._data._check_compatible_with(value) - - elif isinstance(value, self._data._recognized_scalars): - self._data._check_compatible_with(value) - value = self._data._scalar_type(value) - - elif not isinstance(value, DatetimeArray): - raise TypeError( - "searchsorted requires compatible dtype or scalar, " - f"not {type(value).__name__}" - ) - - return self._data.searchsorted(value, side=side) - def is_type_compatible(self, typ) -> bool: return typ == self.inferred_type or typ == "datetime" diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 1ded13abb7cbc..1aa045e38a074 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -4,7 +4,7 @@ import numpy as np from pandas._libs import NaT, Timedelta, index as libindex -from pandas.util._decorators import Appender, Substitution +from pandas.util._decorators import Appender from pandas.core.dtypes.common import ( _TD_DTYPE, @@ -21,7 +21,6 @@ from pandas.core.accessor import delegate_names from pandas.core.arrays import datetimelike as dtl from pandas.core.arrays.timedeltas import TimedeltaArray, _is_convertible_to_td -from pandas.core.base import _shared_docs import pandas.core.common as com from pandas.core.indexes.base import Index, _index_shared_docs, maybe_extract_name from pandas.core.indexes.datetimelike import ( @@ -344,30 +343,6 @@ def _partial_td_slice(self, key): raise NotImplementedError - @Substitution(klass="TimedeltaIndex") - @Appender(_shared_docs["searchsorted"]) - def searchsorted(self, value, side="left", sorter=None): - if isinstance(value, (np.ndarray, Index)): - if not type(self._data)._is_recognized_dtype(value): - raise TypeError( - "searchsorted requires compatible dtype or scalar, " - f"not {type(value).__name__}" - ) - value = type(self._data)(value) - self._data._check_compatible_with(value) - - elif isinstance(value, self._data._recognized_scalars): - self._data._check_compatible_with(value) - value = self._data._scalar_type(value) - - elif not isinstance(value, TimedeltaArray): - raise TypeError( - "searchsorted requires compatible dtype or scalar, " - f"not {type(value).__name__}" - ) - - return self._data.searchsorted(value, side=side, sorter=sorter) - def is_type_compatible(self, typ) -> bool: return typ == self.inferred_type or typ == "timedelta"