From ddb78de27de1ae292784cb58ad8ad228232cb514 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 12 Feb 2018 20:13:20 -0800 Subject: [PATCH 1/6] Fic docstrings, make error messages class-agnostic --- pandas/core/indexes/base.py | 8 +++ pandas/core/indexes/datetimelike.py | 20 ++++---- pandas/core/indexes/datetimes.py | 75 +++++++++++++++-------------- pandas/core/indexes/timedeltas.py | 49 ++++++++++++------- 4 files changed, 89 insertions(+), 63 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 15df77bf772dc..93e3a51d1cfde 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -188,6 +188,14 @@ class Index(IndexOpsMixin, PandasObject): _accessors = frozenset(['str']) str = CachedAccessor("str", StringMethods) + @property + def _base_constructor(self): + """ + _base_constructor returns a base class for type(self) to create + a new object of similar type, but possibly with a different dtype. + """ + return Index + def __new__(cls, data=None, dtype=None, copy=False, name=None, fastpath=False, tupleize_cols=True, **kwargs): diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 4a526955d9bf4..691c7d4fb3c57 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -627,9 +627,9 @@ def _convert_scalar_indexer(self, key, kind=None): ._convert_scalar_indexer(key, kind=kind)) def _add_datelike(self, other): - raise TypeError("cannot add {0} and {1}" - .format(type(self).__name__, - type(other).__name__)) + raise TypeError("cannot add {cls} and {typ}" + .format(cls=type(self).__name__, + typ=type(other).__name__)) def _sub_datelike(self, other): raise com.AbstractMethodError(self) @@ -670,8 +670,9 @@ def __add__(self, other): elif isinstance(self, TimedeltaIndex) and isinstance(other, Index): if hasattr(other, '_add_delta'): return other._add_delta(self) - raise TypeError("cannot add TimedeltaIndex and {typ}" - .format(typ=type(other))) + raise TypeError("cannot add {cls} and {typ}" + .format(cls=type(self).__name__, + typ=type(other).__name__)) elif is_integer(other): return self.shift(other) elif isinstance(other, (datetime, np.datetime64)): @@ -705,8 +706,9 @@ def __sub__(self, other): return self._sub_offset_array(other) elif isinstance(self, TimedeltaIndex) and isinstance(other, Index): if not isinstance(other, TimedeltaIndex): - raise TypeError("cannot subtract TimedeltaIndex and {typ}" - .format(typ=type(other).__name__)) + raise TypeError("cannot subtract {cls} and {typ}" + .format(cls=type(self).__name__, + typ=type(other).__name__)) return self._add_delta(-other) elif isinstance(other, DatetimeIndex): return self._sub_datelike(other) @@ -794,7 +796,7 @@ def isin(self, values): def shift(self, n, freq=None): """ - Specialized shift which produces a DatetimeIndex + Specialized shift which produces a new object of the same type as self Parameters ---------- @@ -804,7 +806,7 @@ def shift(self, n, freq=None): Returns ------- - shifted : DatetimeIndex + shifted : type(self) """ if freq is not None and freq != self.freq: if isinstance(freq, compat.string_types): diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 61c941c3d2333..3bcdcde8d04ec 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -37,7 +37,7 @@ from pandas.core.algorithms import checked_add_with_arr from pandas.core.indexes.base import Index, _index_shared_docs -from pandas.core.indexes.numeric import Int64Index, Float64Index +from pandas.core.indexes.numeric import Int64Index import pandas.compat as compat from pandas.tseries.frequencies import to_offset, get_period_alias, Resolution from pandas.core.indexes.datetimelike import ( @@ -93,7 +93,7 @@ def f(self): result = fields.get_date_field(values, field) result = self._maybe_mask_results(result, convert='float64') - return Index(result, name=self.name) + return self._base_constructor(result, name=self.name) f.__name__ = name f.__doc__ = docstring @@ -119,7 +119,7 @@ def wrapper(self, other): result.fill(nat_result) else: if isinstance(other, list): - other = DatetimeIndex(other) + other = self.__class__(other) elif not isinstance(other, (np.datetime64, np.ndarray, Index, ABCSeries)): # Following Timestamp convention, __eq__ is all-False @@ -151,7 +151,7 @@ def wrapper(self, other): # support of bool dtype indexers if is_bool_dtype(result): return result - return Index(result) + return self._base_constructor(result) return compat.set_function_name(wrapper, opname, cls) @@ -826,18 +826,18 @@ def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike if other is libts.NaT: return self._nat_new(box=True) - raise TypeError("cannot add {0} and {1}" - .format(type(self).__name__, - type(other).__name__)) + raise TypeError("cannot add {cls} and {typ}" + .format(cls=type(self).__name__, + typ=type(other).__name__)) def _sub_datelike(self, other): # subtract a datetime from myself, yielding a TimedeltaIndex - from pandas import TimedeltaIndex if isinstance(other, DatetimeIndex): # require tz compat if not self._has_same_tz(other): - raise TypeError("DatetimeIndex subtraction must have the same " - "timezones or no timezones") + raise TypeError("{cls} subtraction must have the same " + "timezones or no timezones" + .format(cls=type(self).__name__)) result = self._sub_datelike_dti(other) elif isinstance(other, (datetime, np.datetime64)): other = Timestamp(other) @@ -854,9 +854,11 @@ def _sub_datelike(self, other): result = self._maybe_mask_results(result, fill_value=libts.iNaT) else: - raise TypeError("cannot subtract DatetimeIndex and {typ}" - .format(typ=type(other).__name__)) - return TimedeltaIndex(result, name=self.name, copy=False) + raise TypeError("cannot subtract {cls} and {typ}" + .format(cls=type(self).__name__, + typ=type(other).__name__)) + return self._base_constructor(result, name=self.name, + copy=False, dtype='timedelta64[ns]') def _sub_datelike_dti(self, other): """subtraction of two DatetimeIndexes""" @@ -890,7 +892,7 @@ def _add_delta(self, delta): new_values = self._add_delta_td(delta) elif is_timedelta64_dtype(delta): if not isinstance(delta, TimedeltaIndex): - delta = TimedeltaIndex(delta) + delta = self._base_constructor(delta, dtype='timedelta64[ns]') else: # update name when delta is Index name = com._maybe_match_name(self, delta) @@ -901,7 +903,7 @@ def _add_delta(self, delta): new_values = self.astype('O') + delta tz = 'UTC' if self.tz is not None else None - result = DatetimeIndex(new_values, tz=tz, name=name, freq='infer') + result = self.__class__(new_values, tz=tz, name=name, freq='infer') if self.tz is not None and self.tz is not utc: result = result.tz_convert(self.tz) return result @@ -918,8 +920,8 @@ def _add_offset(self, offset): return result except NotImplementedError: - warnings.warn("Non-vectorized DateOffset being applied to Series " - "or DatetimeIndex", PerformanceWarning) + warnings.warn("Non-vectorized DateOffset being applied to {cls}" + .format(cls=type(self).__name__), PerformanceWarning) return self.astype('O') + offset def _add_offset_array(self, other): @@ -930,8 +932,8 @@ def _add_offset_array(self, other): return self + other[0] else: warnings.warn("Adding/subtracting array of DateOffsets to " - "{} not vectorized".format(type(self)), - PerformanceWarning) + "{cls} not vectorized" + .format(cls=type(self).__name__), PerformanceWarning) return self.astype('O') + np.array(other) # TODO: This works for __add__ but loses dtype in __sub__ @@ -943,8 +945,8 @@ def _sub_offset_array(self, other): return self - other[0] else: warnings.warn("Adding/subtracting array of DateOffsets to " - "{} not vectorized".format(type(self)), - PerformanceWarning) + "{cls} not vectorized" + .format(cls=type(self).__name__), PerformanceWarning) res_values = self.astype('O').values - np.array(other) return self.__class__(res_values, freq='infer') @@ -1753,8 +1755,8 @@ def normalize(self): normalized : DatetimeIndex """ new_values = conversion.date_normalize(self.asi8, self.tz) - return DatetimeIndex(new_values, freq='infer', name=self.name, - tz=self.tz) + return self.__class__(new_values, freq='infer', + name=self.name, tz=self.tz) @Substitution(klass='DatetimeIndex') @Appender(_shared_docs['searchsorted']) @@ -2060,19 +2062,20 @@ def to_julian_date(self): testarr = month < 3 year[testarr] -= 1 month[testarr] += 12 - return Float64Index(day + - np.fix((153 * month - 457) / 5) + - 365 * year + - np.floor(year / 4) - - np.floor(year / 100) + - np.floor(year / 400) + - 1721118.5 + - (self.hour + - self.minute / 60.0 + - self.second / 3600.0 + - self.microsecond / 3600.0 / 1e+6 + - self.nanosecond / 3600.0 / 1e+9 - ) / 24.0) + result = (day + + np.fix((153 * month - 457) / 5) + + 365 * year + + np.floor(year / 4) - + np.floor(year / 100) + + np.floor(year / 400) + + 1721118.5 + + (self.hour + + self.minute / 60.0 + + self.second / 3600.0 + + self.microsecond / 3600.0 / 1e+6 + + self.nanosecond / 3600.0 / 1e+9 + ) / 24.0) + return self._base_constructor(result, dtype=np.float64) DatetimeIndex._add_comparison_methods() diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 4b543262fc485..1aba3de7bb303 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -46,7 +46,7 @@ def f(self): if self.hasnans: result = self._maybe_mask_results(result, convert='float64') - return Index(result, name=self.name) + return self._base_constructor(result, name=self.name) f.__name__ = name f.__doc__ = docstring @@ -74,7 +74,7 @@ def wrapper(self, other): if not is_list_like(other): raise TypeError(msg.format(type(other))) - other = TimedeltaIndex(other).values + other = self.__class__(other).values result = func(other) result = com._values_from_object(result) @@ -92,7 +92,7 @@ def wrapper(self, other): # support of bool dtype indexers if is_bool_dtype(result): return result - return Index(result) + return self._base_constructor(result) return compat.set_function_name(wrapper, opname, cls) @@ -364,10 +364,11 @@ def _add_delta(self, delta): # update name when delta is index name = com._maybe_match_name(self, delta) else: - raise TypeError("cannot add the type {0} to a TimedeltaIndex" - .format(type(delta))) + raise TypeError("cannot add the type {typ} to a {cls}" + .format(typ=type(delta).__name__, + cls=type(self).__name__)) - result = TimedeltaIndex(new_values, freq='infer', name=name) + result = self.__class__(new_values, freq='infer', name=name) return result def _evaluate_with_timedelta_like(self, other, op, opstr, reversed=False): @@ -393,13 +394,14 @@ def _evaluate_with_timedelta_like(self, other, op, opstr, reversed=False): else: result = op(left, np.float64(right)) result = self._maybe_mask_results(result, convert='float64') - return Index(result, name=self.name, copy=False) + return self._base_constructor(result, + name=self.name, copy=False) return NotImplemented def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike - from pandas import Timestamp, DatetimeIndex + from pandas import Timestamp if other is NaT: # GH#19124 pd.NaT is treated like a timedelta return self._nat_new() @@ -409,7 +411,14 @@ def _add_datelike(self, other): result = checked_add_with_arr(i8, other.value, arr_mask=self._isnan) result = self._maybe_mask_results(result, fill_value=iNaT) - return DatetimeIndex(result, name=self.name, copy=False) + dtype = 'datetime64[ns]' + if other.tz is not None: + # TODO: This should be a function in dtypes.dtypes(?) + from pandas.core.dtypes.dtypes import DatetimeTZDtype + dtype = DatetimeTZDtype(unit='ns', tz=other.tz) + return self._base_constructor(result, + name=self.name, copy=False, + dtype=dtype, tz=other.tz) def _sub_datelike(self, other): # GH#19124 Timedelta - datetime is not in general well-defined. @@ -418,7 +427,8 @@ def _sub_datelike(self, other): if other is NaT: return self._nat_new() else: - raise TypeError("cannot subtract a datelike from a TimedeltaIndex") + raise TypeError("cannot subtract a datelike from a {cls}" + .format(cls=type(self).__name__)) def _add_offset_array(self, other): # Array/Index of DateOffset objects @@ -433,12 +443,14 @@ def _add_offset_array(self, other): else: from pandas.errors import PerformanceWarning warnings.warn("Adding/subtracting array of DateOffsets to " - "{} not vectorized".format(type(self)), + "{cls} not vectorized" + .format(cls=type(self).__name__), PerformanceWarning) return self.astype('O') + np.array(other) # TODO: This works for __add__ but loses dtype in __sub__ except AttributeError: - raise TypeError("Cannot add non-tick DateOffset to TimedeltaIndex") + raise TypeError("Cannot add non-tick DateOffset to {cls}" + .format(cls=type(self).__name__)) def _sub_offset_array(self, other): # Array/Index of DateOffset objects @@ -453,13 +465,14 @@ def _sub_offset_array(self, other): else: from pandas.errors import PerformanceWarning warnings.warn("Adding/subtracting array of DateOffsets to " - "{} not vectorized".format(type(self)), + "{cls} not vectorized" + .format(cls=type(self).__name__), PerformanceWarning) res_values = self.astype('O').values - np.array(other) return self.__class__(res_values, freq='infer') except AttributeError: - raise TypeError("Cannot subtrack non-tick DateOffset from" - " TimedeltaIndex") + raise TypeError("Cannot subtrack non-tick DateOffset " + "from {cls}".format(cls=type(self).__name__)) def _format_native_types(self, na_rep=u('NaT'), date_format=None, **kwargs): @@ -514,8 +527,8 @@ def total_seconds(self): """ Total duration of each element expressed in seconds. """ - return Index(self._maybe_mask_results(1e-9 * self.asi8), - name=self.name) + result = self._maybe_mask_results(1e-9 * self.asi8) + return self._base_constructor(result, name=self.name) def to_pytimedelta(self): """ @@ -928,7 +941,7 @@ def insert(self, loc, item): def delete(self, loc): """ - Make a new DatetimeIndex with passed location(s) deleted. + Make a new TimedeltaIndex with passed location(s) deleted. Parameters ---------- From 08d575ede99a1dbde0b266f072436b749ddb7907 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 12 Feb 2018 21:32:31 -0800 Subject: [PATCH 2/6] revert some less obvious changes --- pandas/core/indexes/base.py | 8 ------ pandas/core/indexes/datetimes.py | 47 +++++++++++++++---------------- pandas/core/indexes/timedeltas.py | 26 ++++++----------- 3 files changed, 32 insertions(+), 49 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 93e3a51d1cfde..15df77bf772dc 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -188,14 +188,6 @@ class Index(IndexOpsMixin, PandasObject): _accessors = frozenset(['str']) str = CachedAccessor("str", StringMethods) - @property - def _base_constructor(self): - """ - _base_constructor returns a base class for type(self) to create - a new object of similar type, but possibly with a different dtype. - """ - return Index - def __new__(cls, data=None, dtype=None, copy=False, name=None, fastpath=False, tupleize_cols=True, **kwargs): diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 3bcdcde8d04ec..c83310ab375db 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -37,7 +37,7 @@ from pandas.core.algorithms import checked_add_with_arr from pandas.core.indexes.base import Index, _index_shared_docs -from pandas.core.indexes.numeric import Int64Index +from pandas.core.indexes.numeric import Int64Index, Float64Index import pandas.compat as compat from pandas.tseries.frequencies import to_offset, get_period_alias, Resolution from pandas.core.indexes.datetimelike import ( @@ -93,7 +93,7 @@ def f(self): result = fields.get_date_field(values, field) result = self._maybe_mask_results(result, convert='float64') - return self._base_constructor(result, name=self.name) + return Index(result, name=self.name) f.__name__ = name f.__doc__ = docstring @@ -119,7 +119,7 @@ def wrapper(self, other): result.fill(nat_result) else: if isinstance(other, list): - other = self.__class__(other) + other = DatetimeIndex(other) elif not isinstance(other, (np.datetime64, np.ndarray, Index, ABCSeries)): # Following Timestamp convention, __eq__ is all-False @@ -151,7 +151,7 @@ def wrapper(self, other): # support of bool dtype indexers if is_bool_dtype(result): return result - return self._base_constructor(result) + return Index(result) return compat.set_function_name(wrapper, opname, cls) @@ -832,6 +832,7 @@ def _add_datelike(self, other): def _sub_datelike(self, other): # subtract a datetime from myself, yielding a TimedeltaIndex + from pandas import TimedeltaIndex if isinstance(other, DatetimeIndex): # require tz compat if not self._has_same_tz(other): @@ -857,8 +858,7 @@ def _sub_datelike(self, other): raise TypeError("cannot subtract {cls} and {typ}" .format(cls=type(self).__name__, typ=type(other).__name__)) - return self._base_constructor(result, name=self.name, - copy=False, dtype='timedelta64[ns]') + return TimedeltaIndex(result, name=self.name, copy=False) def _sub_datelike_dti(self, other): """subtraction of two DatetimeIndexes""" @@ -892,7 +892,7 @@ def _add_delta(self, delta): new_values = self._add_delta_td(delta) elif is_timedelta64_dtype(delta): if not isinstance(delta, TimedeltaIndex): - delta = self._base_constructor(delta, dtype='timedelta64[ns]') + delta = TimedeltaIndex(delta) else: # update name when delta is Index name = com._maybe_match_name(self, delta) @@ -903,7 +903,7 @@ def _add_delta(self, delta): new_values = self.astype('O') + delta tz = 'UTC' if self.tz is not None else None - result = self.__class__(new_values, tz=tz, name=name, freq='infer') + result = DatetimeIndex(new_values, tz=tz, name=name, freq='infer') if self.tz is not None and self.tz is not utc: result = result.tz_convert(self.tz) return result @@ -1755,8 +1755,8 @@ def normalize(self): normalized : DatetimeIndex """ new_values = conversion.date_normalize(self.asi8, self.tz) - return self.__class__(new_values, freq='infer', - name=self.name, tz=self.tz) + return DatetimeIndex(new_values, freq='infer', name=self.name, + tz=self.tz) @Substitution(klass='DatetimeIndex') @Appender(_shared_docs['searchsorted']) @@ -2062,20 +2062,19 @@ def to_julian_date(self): testarr = month < 3 year[testarr] -= 1 month[testarr] += 12 - result = (day + - np.fix((153 * month - 457) / 5) + - 365 * year + - np.floor(year / 4) - - np.floor(year / 100) + - np.floor(year / 400) + - 1721118.5 + - (self.hour + - self.minute / 60.0 + - self.second / 3600.0 + - self.microsecond / 3600.0 / 1e+6 + - self.nanosecond / 3600.0 / 1e+9 - ) / 24.0) - return self._base_constructor(result, dtype=np.float64) + return Float64Index(day + + np.fix((153 * month - 457) / 5) + + 365 * year + + np.floor(year / 4) - + np.floor(year / 100) + + np.floor(year / 400) + + 1721118.5 + + (self.hour + + self.minute / 60.0 + + self.second / 3600.0 + + self.microsecond / 3600.0 / 1e+6 + + self.nanosecond / 3600.0 / 1e+9 + ) / 24.0) DatetimeIndex._add_comparison_methods() diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 1aba3de7bb303..1a43296302763 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -46,7 +46,7 @@ def f(self): if self.hasnans: result = self._maybe_mask_results(result, convert='float64') - return self._base_constructor(result, name=self.name) + return Index(result, name=self.name) f.__name__ = name f.__doc__ = docstring @@ -74,7 +74,7 @@ def wrapper(self, other): if not is_list_like(other): raise TypeError(msg.format(type(other))) - other = self.__class__(other).values + other = TimedeltaIndex(other).values result = func(other) result = com._values_from_object(result) @@ -92,7 +92,7 @@ def wrapper(self, other): # support of bool dtype indexers if is_bool_dtype(result): return result - return self._base_constructor(result) + return Index(result) return compat.set_function_name(wrapper, opname, cls) @@ -368,7 +368,7 @@ def _add_delta(self, delta): .format(typ=type(delta).__name__, cls=type(self).__name__)) - result = self.__class__(new_values, freq='infer', name=name) + result = TimedeltaIndex(new_values, freq='infer', name=name) return result def _evaluate_with_timedelta_like(self, other, op, opstr, reversed=False): @@ -394,14 +394,13 @@ def _evaluate_with_timedelta_like(self, other, op, opstr, reversed=False): else: result = op(left, np.float64(right)) result = self._maybe_mask_results(result, convert='float64') - return self._base_constructor(result, - name=self.name, copy=False) + return Index(result, name=self.name, copy=False) return NotImplemented def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike - from pandas import Timestamp + from pandas import Timestamp, DatetimeIndex if other is NaT: # GH#19124 pd.NaT is treated like a timedelta return self._nat_new() @@ -411,14 +410,7 @@ def _add_datelike(self, other): result = checked_add_with_arr(i8, other.value, arr_mask=self._isnan) result = self._maybe_mask_results(result, fill_value=iNaT) - dtype = 'datetime64[ns]' - if other.tz is not None: - # TODO: This should be a function in dtypes.dtypes(?) - from pandas.core.dtypes.dtypes import DatetimeTZDtype - dtype = DatetimeTZDtype(unit='ns', tz=other.tz) - return self._base_constructor(result, - name=self.name, copy=False, - dtype=dtype, tz=other.tz) + return DatetimeIndex(result, name=self.name, copy=False) def _sub_datelike(self, other): # GH#19124 Timedelta - datetime is not in general well-defined. @@ -527,8 +519,8 @@ def total_seconds(self): """ Total duration of each element expressed in seconds. """ - result = self._maybe_mask_results(1e-9 * self.asi8) - return self._base_constructor(result, name=self.name) + return Index(self._maybe_mask_results(1e-9 * self.asi8), + name=self.name) def to_pytimedelta(self): """ From f0d203f940ae4c5a1062f7d3c3e15e6c4f843cb9 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 12 Feb 2018 21:34:49 -0800 Subject: [PATCH 3/6] one more formatter --- pandas/core/indexes/timedeltas.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 1a43296302763..fee541823ffc4 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -59,20 +59,22 @@ def _td_index_cmp(opname, cls, nat_result=False): """ def wrapper(self, other): - msg = "cannot compare a TimedeltaIndex with type {0}" + msg = "cannot compare a {cls} with type {typ}" func = getattr(super(TimedeltaIndex, self), opname) if _is_convertible_to_td(other) or other is NaT: try: other = _to_m8(other) except ValueError: # failed to parse as timedelta - raise TypeError(msg.format(type(other))) + raise TypeError(msg.format(cls=type(self).__name__, + typ=type(other).__name__)) result = func(other) if isna(other): result.fill(nat_result) else: if not is_list_like(other): - raise TypeError(msg.format(type(other))) + raise TypeError(msg.format(cls=type(self).__name__, + typ=type(other).__name__)) other = TimedeltaIndex(other).values result = func(other) From a7775ea9c4d1c5f6fcd474f8f4c56c936868ed2f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 12 Feb 2018 21:39:13 -0800 Subject: [PATCH 4/6] make inheritance more transparent --- pandas/core/indexes/datetimes.py | 6 +++--- pandas/core/indexes/timedeltas.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index c83310ab375db..ca93a5ccf8703 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -106,7 +106,7 @@ def _dt_index_cmp(opname, cls, nat_result=False): """ def wrapper(self, other): - func = getattr(super(DatetimeIndex, self), opname) + binop = getattr(Index, opname) if isinstance(other, (datetime, compat.string_types)): if isinstance(other, datetime): @@ -114,7 +114,7 @@ def wrapper(self, other): self._assert_tzawareness_compat(other) other = _to_m8(other, tz=self.tz) - result = func(other) + result = binop(self, other) if isna(other): result.fill(nat_result) else: @@ -134,7 +134,7 @@ def wrapper(self, other): if is_datetimelike(other): self._assert_tzawareness_compat(other) - result = func(np.asarray(other)) + result = binop(self, np.asarray(other)) result = com._values_from_object(result) if isinstance(other, Index): diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index fee541823ffc4..a80e810abaf7f 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -60,7 +60,7 @@ def _td_index_cmp(opname, cls, nat_result=False): def wrapper(self, other): msg = "cannot compare a {cls} with type {typ}" - func = getattr(super(TimedeltaIndex, self), opname) + binop = getattr(Index, opname) if _is_convertible_to_td(other) or other is NaT: try: other = _to_m8(other) @@ -68,7 +68,7 @@ def wrapper(self, other): # failed to parse as timedelta raise TypeError(msg.format(cls=type(self).__name__, typ=type(other).__name__)) - result = func(other) + result = binop(self, other) if isna(other): result.fill(nat_result) else: @@ -77,7 +77,7 @@ def wrapper(self, other): typ=type(other).__name__)) other = TimedeltaIndex(other).values - result = func(other) + result = binop(self, other) result = com._values_from_object(result) if isinstance(other, Index): From c779f64241d7706d6f7464df50d50a3929a8505b Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Feb 2018 07:36:40 -0800 Subject: [PATCH 5/6] improve DTI/TDI shift docstring --- pandas/core/indexes/datetimelike.py | 4 ++-- pandas/core/indexes/datetimes.py | 5 +++++ pandas/core/indexes/timedeltas.py | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 691c7d4fb3c57..340c176789f14 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -796,7 +796,7 @@ def isin(self, values): def shift(self, n, freq=None): """ - Specialized shift which produces a new object of the same type as self + Specialized shift which produces a %(klass)s Parameters ---------- @@ -806,7 +806,7 @@ def shift(self, n, freq=None): Returns ------- - shifted : type(self) + shifted : %(klass)s """ if freq is not None and freq != self.freq: if isinstance(freq, compat.string_types): diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index ca93a5ccf8703..3e9021b27469a 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -873,6 +873,11 @@ def _sub_datelike_dti(self, other): new_values[mask] = libts.iNaT return new_values.view('i8') + @Substitution(klass='DatetimeIndex') + @Appender(DatetimeIndexOpsMixin.shift.__doc__) + def shift(self, n, freq=None): + return super(DatetimeIndex, self).shift(n, freq) + def _maybe_update_attributes(self, attrs): """ Update Index attributes (e.g. freq) depending on op """ freq = attrs.get('freq', None) diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index a80e810abaf7f..cd9f08a3e7f16 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -468,6 +468,11 @@ def _sub_offset_array(self, other): raise TypeError("Cannot subtrack non-tick DateOffset " "from {cls}".format(cls=type(self).__name__)) + @Substitution(klass='TimedeltaIndex') + @Appender(DatetimeIndexOpsMixin.shift.__doc__) + def shift(self, n, freq=None): + return super(TimedeltaIndex, self).shift(n, freq) + def _format_native_types(self, na_rep=u('NaT'), date_format=None, **kwargs): from pandas.io.formats.format import Timedelta64Formatter From 5c2868a5204b712f7c4a99a1ed0a8dd4d68d1c1f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Feb 2018 11:27:04 -0800 Subject: [PATCH 6/6] fix unreached NameError --- pandas/core/indexes/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 15df77bf772dc..7a9719d3238d5 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3886,7 +3886,7 @@ def _evaluate_with_datetime_like(self, other, op, opstr): raise TypeError("can only perform ops with datetime like values") def _evaluate_compare(self, op): - raise base.AbstractMethodError(self) + raise com.AbstractMethodError(self) @classmethod def _add_comparison_methods(cls):