Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REF: Stop mixing DTA/TDA into DTI/TDI #24476

Merged
merged 25 commits into from
Dec 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
350b0ec
implement _index_data parts of #24024
jbrockmendel Dec 21, 2018
837c16a
implement _eadata, dispatch arithmetic methods to it
jbrockmendel Dec 21, 2018
e28ff51
dont mix DatetimeLikeArrayMixin into DatetimeIndexOpsMixin
jbrockmendel Dec 21, 2018
ba61a0d
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 21, 2018
fdf1770
dont inherit TimedeltaIndex from TimedeltaArray
jbrockmendel Dec 21, 2018
ed9e1de
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 21, 2018
ea3965d
dont inherit from DatetimeArray
jbrockmendel Dec 22, 2018
8ceab31
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 22, 2018
5b95d78
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 24, 2018
60cd35d
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 24, 2018
bbbd778
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 28, 2018
238b386
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 28, 2018
9d01424
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 28, 2018
a5e5d65
use ea_passthrough
jbrockmendel Dec 28, 2018
21833f3
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 28, 2018
1ff0c4d
remove previously-overriden overridings
jbrockmendel Dec 28, 2018
3faed22
stop double-mixing
jbrockmendel Dec 28, 2018
e607edd
stop over-writing
jbrockmendel Dec 28, 2018
38e4bca
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 28, 2018
2afd6ab
handle+test object arrays
jbrockmendel Dec 29, 2018
43a162e
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 29, 2018
9ddf4bd
Remove unused import
jbrockmendel Dec 29, 2018
11dcef0
flake8 fixup
jbrockmendel Dec 29, 2018
6627f56
Merge branch 'master' of https://github.com/pandas-dev/pandas into in…
jbrockmendel Dec 29, 2018
f6a8951
edits per comments
jbrockmendel Dec 29, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 7 additions & 23 deletions pandas/core/arrays/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ def wrapper(self, other):
return ops.invalid_comparison(self, other, op)

if is_object_dtype(other):
result = op(self.astype('O'), np.array(other))
# We have to use _comp_method_OBJECT_ARRAY instead of numpy
# comparison otherwise it would fail to raise when
# comparing tz-aware and tz-naive
with np.errstate(all='ignore'):
result = ops._comp_method_OBJECT_ARRAY(op,
self.astype(object),
other)
o_mask = isna(other)
elif not (is_datetime64_dtype(other) or
is_datetime64tz_dtype(other)):
Expand Down Expand Up @@ -430,28 +436,6 @@ def _timezone(self):
"""
return timezones.get_timezone(self.tzinfo)

@property
def offset(self):
"""
get/set the frequency of the instance
"""
msg = ('{cls}.offset has been deprecated and will be removed '
'in a future version; use {cls}.freq instead.'
.format(cls=type(self).__name__))
warnings.warn(msg, FutureWarning, stacklevel=2)
return self.freq

@offset.setter
def offset(self, value):
"""
get/set the frequency of the instance
"""
msg = ('{cls}.offset has been deprecated and will be removed '
'in a future version; use {cls}.freq instead.'
.format(cls=type(self).__name__))
warnings.warn(msg, FutureWarning, stacklevel=2)
self.freq = value

@property # NB: override with cache_readonly in immutable subclasses
def is_normalized(self):
"""
Expand Down
49 changes: 38 additions & 11 deletions pandas/core/indexes/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from pandas._libs import NaT, iNaT, lib
from pandas.compat.numpy import function as nv
from pandas.errors import AbstractMethodError
from pandas.util._decorators import Appender, cache_readonly
from pandas.util._decorators import Appender, cache_readonly, deprecate_kwarg

from pandas.core.dtypes.common import (
ensure_int64, is_bool_dtype, is_dtype_equal, is_float, is_integer,
Expand All @@ -19,6 +19,7 @@

from pandas.core import algorithms, ops
from pandas.core.accessor import PandasDelegate
from pandas.core.arrays import ExtensionOpsMixin
from pandas.core.arrays.datetimelike import (
DatetimeLikeArrayMixin, _ensure_datetimelike_to_i8)
import pandas.core.indexes.base as ibase
Expand All @@ -30,15 +31,30 @@
_index_doc_kwargs = dict(ibase._index_doc_kwargs)


class DatetimeIndexOpsMixin(DatetimeLikeArrayMixin):
def ea_passthrough(name):
TomAugspurger marked this conversation as resolved.
Show resolved Hide resolved
"""
common ops mixin to support a unified interface datetimelike Index
Make an alias for a method of the underlying ExtensionArray.

Parameters
----------
name : str

Returns
-------
method
"""
def method(self, *args, **kwargs):
return getattr(self._eadata, name)(*args, **kwargs)

method.__name__ = name
# TODO: docstrings
return method


# override DatetimeLikeArrayMixin method
copy = Index.copy
view = Index.view
__setitem__ = Index.__setitem__
class DatetimeIndexOpsMixin(ExtensionOpsMixin):
"""
common ops mixin to support a unified interface datetimelike Index
"""

# DatetimeLikeArrayMixin assumes subclasses are mutable, so these are
# properties there. They can be made into cache_readonly for Index
Expand All @@ -50,6 +66,14 @@ class DatetimeIndexOpsMixin(DatetimeLikeArrayMixin):
_resolution = cache_readonly(DatetimeLikeArrayMixin._resolution.fget)
resolution = cache_readonly(DatetimeLikeArrayMixin.resolution.fget)

_box_values = ea_passthrough("_box_values")
_maybe_mask_results = ea_passthrough("_maybe_mask_results")
__iter__ = ea_passthrough("__iter__")

@property
def freqstr(self):
return self._eadata.freqstr

def unique(self, level=None):
if level is not None:
self._validate_index_level(level)
Expand All @@ -74,9 +98,6 @@ def wrapper(self, other):
wrapper.__name__ = '__{}__'.format(op.__name__)
return wrapper

# A few methods that are shared
_maybe_mask_results = DatetimeLikeArrayMixin._maybe_mask_results

# ------------------------------------------------------------------------

def equals(self, other):
Expand Down Expand Up @@ -549,7 +570,7 @@ def _concat_same_dtype(self, to_concat, name):
# - remove the .asi8 here
# - remove the _maybe_box_as_values
# - combine with the `else` block
new_data = self._concat_same_type(to_concat).asi8
new_data = self._eadata._concat_same_type(to_concat).asi8
else:
new_data = type(self._values)._concat_same_type(to_concat)

Expand Down Expand Up @@ -581,6 +602,12 @@ def _time_shift(self, periods, freq=None):
result = self._eadata._time_shift(periods, freq=freq)
return type(self)(result, name=self.name)

@deprecate_kwarg(old_arg_name='n', new_arg_name='periods')
@Appender(DatetimeLikeArrayMixin.shift.__doc__)
def shift(self, periods, freq=None):
result = self._eadata.shift(periods, freq=freq)
return type(self)(result, name=self.name)


def wrap_arithmetic_op(self, other, result):
if result is NotImplemented:
Expand Down
88 changes: 72 additions & 16 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import pandas.core.common as com
from pandas.core.indexes.base import Index
from pandas.core.indexes.datetimelike import (
DatetimeIndexOpsMixin, DatetimelikeDelegateMixin)
DatetimeIndexOpsMixin, DatetimelikeDelegateMixin, ea_passthrough)
from pandas.core.indexes.numeric import Int64Index
from pandas.core.ops import get_op_result_name
import pandas.core.tools.datetimes as tools
Expand Down Expand Up @@ -96,19 +96,13 @@ class DatetimeDelegateMixin(DatetimelikeDelegateMixin):
_delegate_class = DatetimeArray


@delegate_names(DatetimeArray, ["to_period", "tz_localize", "tz_convert",
"day_name", "month_name"],
typ="method", overwrite=True)
@delegate_names(DatetimeArray,
DatetimeArray._field_ops, typ="property", overwrite=True)
@delegate_names(DatetimeArray,
DatetimeDelegateMixin._delegated_properties,
typ="property")
@delegate_names(DatetimeArray,
DatetimeDelegateMixin._delegated_methods,
typ="method", overwrite=False)
class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index,
DatetimeDelegateMixin):
class DatetimeIndex(DatetimeIndexOpsMixin, Int64Index, DatetimeDelegateMixin):
"""
Immutable ndarray of datetime64 data, represented internally as int64, and
which can be boxed to Timestamp objects that are subclasses of datetime and
Expand Down Expand Up @@ -268,6 +262,7 @@ def _join_i8_wrapper(joinf, **kwargs):
_object_ops = DatetimeArray._object_ops
_field_ops = DatetimeArray._field_ops
_datetimelike_ops = DatetimeArray._datetimelike_ops
_datetimelike_methods = DatetimeArray._datetimelike_methods

# --------------------------------------------------------------------
# Constructors
Expand All @@ -294,8 +289,8 @@ def __new__(cls, data=None,
"endpoints is deprecated. Use "
"`pandas.date_range` instead.",
FutureWarning, stacklevel=2)

return cls(dtarr, name=name)
return cls._simple_new(
dtarr._data, freq=dtarr.freq, tz=dtarr.tz, name=name)

if is_scalar(data):
raise TypeError("{cls}() must be called with a "
Expand Down Expand Up @@ -331,7 +326,11 @@ def _simple_new(cls, values, name=None, freq=None, tz=None, dtype=None):
# DatetimeArray._simple_new will accept either i8 or M8[ns] dtypes
assert isinstance(values, np.ndarray), type(values)

result = super(DatetimeIndex, cls)._simple_new(values, freq, tz)
dtarr = DatetimeArray._simple_new(values, freq=freq, tz=tz)
result = object.__new__(cls)
result._data = dtarr._data
result._freq = dtarr.freq
result._tz = dtarr.tz
result.name = name
# For groupby perf. See note in indexes/base about _index_data
result._index_data = result._data
Expand All @@ -340,6 +339,10 @@ def _simple_new(cls, values, name=None, freq=None, tz=None, dtype=None):

# --------------------------------------------------------------------

@property
def dtype(self):
return self._eadata.dtype

@property
def _values(self):
# tz-naive -> ndarray
Expand All @@ -360,6 +363,8 @@ def tz(self, value):
raise AttributeError("Cannot directly set timezone. Use tz_localize() "
"or tz_convert() as appropriate")

tzinfo = tz

@property
def size(self):
# TODO: Remove this when we have a DatetimeTZArray
Expand Down Expand Up @@ -670,7 +675,7 @@ def intersection(self, other):
def _get_time_micros(self):
values = self.asi8
if self.tz is not None and not timezones.is_utc(self.tz):
values = self._local_timestamps()
values = self._eadata._local_timestamps()
return fields.get_time_micros(values)

def to_series(self, keep_tz=None, index=None, name=None):
Expand Down Expand Up @@ -1139,12 +1144,64 @@ def _eadata(self):
_is_monotonic_increasing = Index.is_monotonic_increasing
_is_monotonic_decreasing = Index.is_monotonic_decreasing
_is_unique = Index.is_unique
astype = DatetimeIndexOpsMixin.astype

_timezone = cache_readonly(DatetimeArray._timezone.fget)
is_normalized = cache_readonly(DatetimeArray.is_normalized.fget)
_resolution = cache_readonly(DatetimeArray._resolution.fget)

strftime = ea_passthrough("strftime")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removed the docstring. Is there a TODO / issue to add it back?

_has_same_tz = ea_passthrough("_has_same_tz")
__array__ = ea_passthrough("__array__")

@property
def offset(self):
"""
get/set the frequency of the instance
"""
msg = ('{cls}.offset has been deprecated and will be removed '
'in a future version; use {cls}.freq instead.'
.format(cls=type(self).__name__))
warnings.warn(msg, FutureWarning, stacklevel=2)
return self.freq

@offset.setter
def offset(self, value):
"""
get/set the frequency of the instance
"""
msg = ('{cls}.offset has been deprecated and will be removed '
'in a future version; use {cls}.freq instead.'
.format(cls=type(self).__name__))
warnings.warn(msg, FutureWarning, stacklevel=2)
self.freq = value

@property
def freq(self):
return self._freq

@freq.setter
def freq(self, value):
if value is not None:
# let DatetimeArray to validation
self._eadata.freq = value

self._freq = to_offset(value)

def __getitem__(self, key):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #24024 we move __getitem__ to the base DatetimeIndexOpsMixin and DatetimeArrayMixin. That seems relatively easy to do if we're already changing __getitem__ here and in TimedeltaIndex, but it may not be possible yet.

result = self._eadata.__getitem__(key)
if is_scalar(result):
return result
elif result.ndim > 1:
# To support MPL which performs slicing with 2 dim
# even though it only has 1 dim by definition
assert isinstance(result, np.ndarray), result
return result
return type(self)(result, name=self.name)

@property
def _box_func(self):
return lambda x: Timestamp(x, tz=self.tz)

# --------------------------------------------------------------------

@Substitution(klass='DatetimeIndex')
Expand Down Expand Up @@ -1486,9 +1543,8 @@ def date_range(start=None, end=None, periods=None, freq=None, tz=None,
start=start, end=end, periods=periods,
freq=freq, tz=tz, normalize=normalize,
closed=closed, **kwargs)

result = DatetimeIndex(dtarr, name=name)
return result
return DatetimeIndex._simple_new(
dtarr._data, tz=dtarr.tz, freq=dtarr.freq, name=name)


def bdate_range(start=None, end=None, periods=None, freq='B', tz=None,
Expand Down
Loading