Skip to content

Commit 672ae96

Browse files
jbrockmendelPingviinituutti
authored andcommitted
implement Delegate portions of pandas-dev#24024 (pandas-dev#24463)
1 parent 9c17251 commit 672ae96

File tree

4 files changed

+100
-119
lines changed

4 files changed

+100
-119
lines changed

pandas/core/indexes/accessors.py

+11-9
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212
from pandas.core.accessor import PandasDelegate, delegate_names
1313
from pandas.core.algorithms import take_1d
14+
from pandas.core.arrays import (
15+
DatetimeArrayMixin as DatetimeArray, PeriodArray,
16+
TimedeltaArrayMixin as TimedeltaArray)
1417
from pandas.core.base import NoNewAttributesMixin, PandasObject
1518
from pandas.core.indexes.datetimes import DatetimeIndex
16-
from pandas.core.indexes.period import PeriodArray
1719
from pandas.core.indexes.timedeltas import TimedeltaIndex
1820

1921

@@ -106,11 +108,11 @@ def _delegate_method(self, name, *args, **kwargs):
106108
return result
107109

108110

109-
@delegate_names(delegate=DatetimeIndex,
110-
accessors=DatetimeIndex._datetimelike_ops,
111+
@delegate_names(delegate=DatetimeArray,
112+
accessors=DatetimeArray._datetimelike_ops,
111113
typ="property")
112-
@delegate_names(delegate=DatetimeIndex,
113-
accessors=DatetimeIndex._datetimelike_methods,
114+
@delegate_names(delegate=DatetimeArray,
115+
accessors=DatetimeArray._datetimelike_methods,
114116
typ="method")
115117
class DatetimeProperties(Properties):
116118
"""
@@ -177,11 +179,11 @@ def freq(self):
177179
return self._get_values().inferred_freq
178180

179181

180-
@delegate_names(delegate=TimedeltaIndex,
181-
accessors=TimedeltaIndex._datetimelike_ops,
182+
@delegate_names(delegate=TimedeltaArray,
183+
accessors=TimedeltaArray._datetimelike_ops,
182184
typ="property")
183-
@delegate_names(delegate=TimedeltaIndex,
184-
accessors=TimedeltaIndex._datetimelike_methods,
185+
@delegate_names(delegate=TimedeltaArray,
186+
accessors=TimedeltaArray._datetimelike_methods,
185187
typ="method")
186188
class TimedeltaProperties(Properties):
187189
"""

pandas/core/indexes/datetimelike.py

+3-60
Original file line numberDiff line numberDiff line change
@@ -594,63 +594,6 @@ def wrap_arithmetic_op(self, other, result):
594594
return result
595595

596596

597-
def wrap_array_method(method, pin_name=False):
598-
"""
599-
Wrap a DatetimeArray/TimedeltaArray/PeriodArray method so that the
600-
returned object is an Index subclass instead of ndarray or ExtensionArray
601-
subclass.
602-
603-
Parameters
604-
----------
605-
method : method of Datetime/Timedelta/Period Array class
606-
pin_name : bool
607-
Whether to set name=self.name on the output Index
608-
609-
Returns
610-
-------
611-
method
612-
"""
613-
def index_method(self, *args, **kwargs):
614-
result = method(self._eadata, *args, **kwargs)
615-
616-
# Index.__new__ will choose the appropriate subclass to return
617-
result = Index(result)
618-
if pin_name:
619-
result.name = self.name
620-
return result
621-
622-
index_method.__name__ = method.__name__
623-
index_method.__doc__ = method.__doc__
624-
return index_method
625-
626-
627-
def wrap_field_accessor(prop):
628-
"""
629-
Wrap a DatetimeArray/TimedeltaArray/PeriodArray array-returning property
630-
to return an Index subclass instead of ndarray or ExtensionArray subclass.
631-
632-
Parameters
633-
----------
634-
prop : property
635-
636-
Returns
637-
-------
638-
new_prop : property
639-
"""
640-
fget = prop.fget
641-
642-
def f(self):
643-
result = fget(self._eadata)
644-
if is_bool_dtype(result):
645-
# return numpy array b/c there is no BoolIndex
646-
return result
647-
return Index(result, name=self.name)
648-
649-
f.__name__ = fget.__name__
650-
f.__doc__ = fget.__doc__
651-
return property(f)
652-
653-
654597
def maybe_unwrap_index(obj):
655598
"""
656599
If operating against another Index object, we need to unwrap the underlying
@@ -703,16 +646,16 @@ def _delegate_class(self):
703646
raise AbstractMethodError
704647

705648
def _delegate_property_get(self, name, *args, **kwargs):
706-
result = getattr(self._data, name)
649+
result = getattr(self._eadata, name)
707650
if name not in self._raw_properties:
708651
result = Index(result, name=self.name)
709652
return result
710653

711654
def _delegate_property_set(self, name, value, *args, **kwargs):
712-
setattr(self._data, name, value)
655+
setattr(self._eadata, name, value)
713656

714657
def _delegate_method(self, name, *args, **kwargs):
715-
result = operator.methodcaller(name, *args, **kwargs)(self._data)
658+
result = operator.methodcaller(name, *args, **kwargs)(self._eadata)
716659
if name not in self._raw_methods:
717660
result = Index(result, name=self.name)
718661
return result

pandas/core/indexes/datetimes.py

+50-40
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020
import pandas.core.dtypes.concat as _concat
2121
from pandas.core.dtypes.missing import isna
2222

23+
from pandas.core.accessor import delegate_names
2324
from pandas.core.arrays.datetimes import (
2425
DatetimeArrayMixin as DatetimeArray, _to_m8)
2526
from pandas.core.base import _shared_docs
2627
import pandas.core.common as com
2728
from pandas.core.indexes.base import Index, _index_shared_docs
2829
from pandas.core.indexes.datetimelike import (
29-
DatetimeIndexOpsMixin, wrap_array_method, wrap_field_accessor)
30+
DatetimeIndexOpsMixin, DatetimelikeDelegateMixin)
3031
from pandas.core.indexes.numeric import Int64Index
3132
from pandas.core.ops import get_op_result_name
3233
import pandas.core.tools.datetimes as tools
@@ -61,7 +62,54 @@ def _new_DatetimeIndex(cls, d):
6162
return result
6263

6364

64-
class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index):
65+
class DatetimeDelegateMixin(DatetimelikeDelegateMixin):
66+
# Most attrs are dispatched via datetimelike_{ops,methods}
67+
# Some are "raw" methods, the result is not not re-boxed in an Index
68+
# We also have a few "extra" attrs, which may or may not be raw,
69+
# which we we dont' want to expose in the .dt accessor.
70+
_extra_methods = [
71+
'to_period',
72+
'to_perioddelta',
73+
'to_julian_date',
74+
]
75+
_extra_raw_methods = [
76+
'to_pydatetime',
77+
'_local_timestamps',
78+
'_has_same_tz',
79+
]
80+
_extra_raw_properties = [
81+
'_box_func',
82+
'tz', 'tzinfo',
83+
]
84+
_delegated_properties = (
85+
DatetimeArray._datetimelike_ops + _extra_raw_properties
86+
)
87+
_delegated_methods = (
88+
DatetimeArray._datetimelike_methods + _extra_methods +
89+
_extra_raw_methods
90+
)
91+
_raw_properties = {
92+
'date',
93+
'time',
94+
'timetz',
95+
} | set(DatetimeArray._bool_ops) | set(_extra_raw_properties)
96+
_raw_methods = set(_extra_raw_methods)
97+
_delegate_class = DatetimeArray
98+
99+
100+
@delegate_names(DatetimeArray, ["to_period", "tz_localize", "tz_convert",
101+
"day_name", "month_name"],
102+
typ="method", overwrite=True)
103+
@delegate_names(DatetimeArray,
104+
DatetimeArray._field_ops, typ="property", overwrite=True)
105+
@delegate_names(DatetimeArray,
106+
DatetimeDelegateMixin._delegated_properties,
107+
typ="property")
108+
@delegate_names(DatetimeArray,
109+
DatetimeDelegateMixin._delegated_methods,
110+
typ="method", overwrite=False)
111+
class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index,
112+
DatetimeDelegateMixin):
65113
"""
66114
Immutable ndarray of datetime64 data, represented internally as int64, and
67115
which can be boxed to Timestamp objects that are subclasses of datetime and
@@ -1094,44 +1142,6 @@ def _eadata(self):
10941142
is_normalized = cache_readonly(DatetimeArray.is_normalized.fget)
10951143
_resolution = cache_readonly(DatetimeArray._resolution.fget)
10961144

1097-
year = wrap_field_accessor(DatetimeArray.year)
1098-
month = wrap_field_accessor(DatetimeArray.month)
1099-
day = wrap_field_accessor(DatetimeArray.day)
1100-
hour = wrap_field_accessor(DatetimeArray.hour)
1101-
minute = wrap_field_accessor(DatetimeArray.minute)
1102-
second = wrap_field_accessor(DatetimeArray.second)
1103-
microsecond = wrap_field_accessor(DatetimeArray.microsecond)
1104-
nanosecond = wrap_field_accessor(DatetimeArray.nanosecond)
1105-
weekofyear = wrap_field_accessor(DatetimeArray.weekofyear)
1106-
week = weekofyear
1107-
dayofweek = wrap_field_accessor(DatetimeArray.dayofweek)
1108-
weekday = dayofweek
1109-
1110-
weekday_name = wrap_field_accessor(DatetimeArray.weekday_name)
1111-
1112-
dayofyear = wrap_field_accessor(DatetimeArray.dayofyear)
1113-
quarter = wrap_field_accessor(DatetimeArray.quarter)
1114-
days_in_month = wrap_field_accessor(DatetimeArray.days_in_month)
1115-
daysinmonth = days_in_month
1116-
is_month_start = wrap_field_accessor(DatetimeArray.is_month_start)
1117-
is_month_end = wrap_field_accessor(DatetimeArray.is_month_end)
1118-
is_quarter_start = wrap_field_accessor(DatetimeArray.is_quarter_start)
1119-
is_quarter_end = wrap_field_accessor(DatetimeArray.is_quarter_end)
1120-
is_year_start = wrap_field_accessor(DatetimeArray.is_year_start)
1121-
is_year_end = wrap_field_accessor(DatetimeArray.is_year_end)
1122-
is_leap_year = wrap_field_accessor(DatetimeArray.is_leap_year)
1123-
1124-
tz_localize = wrap_array_method(DatetimeArray.tz_localize, True)
1125-
tz_convert = wrap_array_method(DatetimeArray.tz_convert, True)
1126-
to_perioddelta = wrap_array_method(DatetimeArray.to_perioddelta,
1127-
False)
1128-
to_period = wrap_array_method(DatetimeArray.to_period, True)
1129-
normalize = wrap_array_method(DatetimeArray.normalize, True)
1130-
to_julian_date = wrap_array_method(DatetimeArray.to_julian_date,
1131-
False)
1132-
month_name = wrap_array_method(DatetimeArray.month_name, True)
1133-
day_name = wrap_array_method(DatetimeArray.day_name, True)
1134-
11351145
# --------------------------------------------------------------------
11361146

11371147
@Substitution(klass='DatetimeIndex')

pandas/core/indexes/timedeltas.py

+36-10
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@
1515
import pandas.core.dtypes.concat as _concat
1616
from pandas.core.dtypes.missing import isna
1717

18+
from pandas.core.accessor import delegate_names
1819
from pandas.core.arrays import datetimelike as dtl
1920
from pandas.core.arrays.timedeltas import (
2021
TimedeltaArrayMixin as TimedeltaArray, _is_convertible_to_td, _to_m8)
2122
from pandas.core.base import _shared_docs
2223
import pandas.core.common as com
2324
from pandas.core.indexes.base import Index, _index_shared_docs
2425
from pandas.core.indexes.datetimelike import (
25-
DatetimeIndexOpsMixin, maybe_unwrap_index, wrap_arithmetic_op,
26-
wrap_array_method, wrap_field_accessor)
26+
DatetimeIndexOpsMixin, DatetimelikeDelegateMixin, maybe_unwrap_index,
27+
wrap_arithmetic_op)
2728
from pandas.core.indexes.numeric import Int64Index
2829
from pandas.core.ops import get_op_result_name
2930
from pandas.core.tools.timedeltas import _coerce_scalar_to_timedelta_type
@@ -43,8 +44,40 @@ def method(self, other):
4344
return method
4445

4546

47+
class TimedeltaDelegateMixin(DatetimelikeDelegateMixin):
48+
# Most attrs are dispatched via datetimelike_{ops,methods}
49+
# Some are "raw" methods, the result is not not re-boxed in an Index
50+
# We also have a few "extra" attrs, which may or may not be raw,
51+
# which we we dont' want to expose in the .dt accessor.
52+
_delegate_class = TimedeltaArray
53+
_delegated_properties = (TimedeltaArray._datetimelike_ops + [
54+
'components',
55+
])
56+
_delegated_methods = TimedeltaArray._datetimelike_methods + [
57+
'_box_values',
58+
]
59+
_raw_properties = {
60+
'components',
61+
}
62+
_raw_methods = {
63+
'to_pytimedelta',
64+
}
65+
66+
67+
@delegate_names(TimedeltaArray,
68+
["to_pytimedelta", "total_seconds"],
69+
typ="method", overwrite=True)
70+
@delegate_names(TimedeltaArray,
71+
["days", "seconds", "microseconds", "nanoseconds"],
72+
typ="property", overwrite=True)
73+
@delegate_names(TimedeltaArray,
74+
TimedeltaDelegateMixin._delegated_properties,
75+
typ="property")
76+
@delegate_names(TimedeltaArray,
77+
TimedeltaDelegateMixin._delegated_methods,
78+
typ="method", overwrite=False)
4679
class TimedeltaIndex(TimedeltaArray, DatetimeIndexOpsMixin,
47-
dtl.TimelikeOps, Int64Index):
80+
dtl.TimelikeOps, Int64Index, TimedeltaDelegateMixin):
4881
"""
4982
Immutable ndarray of timedelta64 data, represented internally as int64, and
5083
which can be boxed to timedelta objects
@@ -251,13 +284,6 @@ def _eadata(self):
251284
__div__ = __truediv__
252285
__rdiv__ = __rtruediv__
253286

254-
days = wrap_field_accessor(TimedeltaArray.days)
255-
seconds = wrap_field_accessor(TimedeltaArray.seconds)
256-
microseconds = wrap_field_accessor(TimedeltaArray.microseconds)
257-
nanoseconds = wrap_field_accessor(TimedeltaArray.nanoseconds)
258-
259-
total_seconds = wrap_array_method(TimedeltaArray.total_seconds, True)
260-
261287
# Compat for frequency inference, see GH#23789
262288
_is_monotonic_increasing = Index.is_monotonic_increasing
263289
_is_monotonic_decreasing = Index.is_monotonic_decreasing

0 commit comments

Comments
 (0)