Skip to content

Commit 101e7e5

Browse files
committed
Transition to _make_accessor
flake8 fixes Update import name
1 parent d152421 commit 101e7e5

File tree

11 files changed

+177
-176
lines changed

11 files changed

+177
-176
lines changed

pandas/core/accessors.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ def _make_accessor(cls, data):
7474
7575
With `_make_accessor` defined, we have enough to create the accessor, but
7676
not enough to actually do anything useful with it. In order to access
77-
*methods* of State objects, we implement `_delegate_method`. `_delegate_method`
78-
calls the underlying method for each object in the series and wraps these
79-
in a new Series. The simplest version looks like:
77+
*methods* of State objects, we implement `_delegate_method`.
78+
`_delegate_method` calls the underlying method for each object in the
79+
series and wraps these in a new Series. The simplest version looks like:
8080
8181
def _delegate_method(self, name, *args, **kwargs):
8282
state_method = lambda x: getattr(x, name)(*args, **kwargs)
@@ -150,6 +150,7 @@ class StateDelegate(PandasDelegate):
150150
from pandas.core.base import PandasObject
151151
from pandas.core import common as com
152152

153+
153154
class PandasDelegate(PandasObject):
154155
""" an abstract base class for delegating methods/properties
155156
@@ -164,12 +165,12 @@ def __init__(self, values):
164165

165166
@classmethod
166167
def _make_accessor(cls, data): # pragma: no cover
167-
raise NotImplementedError('It is up to subclasses to implement '
168+
raise NotImplementedError(
169+
'It is up to subclasses to implement '
168170
'_make_accessor. This does input validation on the object to '
169171
'which the accessor is being pinned. '
170172
'It should return an instance of `cls`.')
171173

172-
173174
def _delegate_property_get(self, name, *args, **kwargs):
174175
raise TypeError("You cannot access the "
175176
"property {name}".format(name=name))
@@ -181,7 +182,6 @@ def _delegate_method(self, name, *args, **kwargs):
181182
raise TypeError("You cannot call method {name}".format(name=name))
182183

183184

184-
185185
class AccessorProperty(object):
186186
"""Descriptor for implementing accessor properties like Series.str
187187
"""
@@ -235,7 +235,6 @@ def _setter(self, new_values):
235235
_doc = getattr(delegate, name).__doc__
236236
return property(fget=_getter, fset=_setter, doc=_doc)
237237

238-
239238
@staticmethod
240239
def create_delegator_method(name, delegate):
241240
# Note: we really only need the `delegate` here for the docstring
@@ -246,14 +245,13 @@ def func(self, *args, **kwargs):
246245
if callable(name):
247246
# A function/method was passed directly instead of a name
248247
# This may also render the `delegate` arg unnecessary.
249-
func.__name__ = name.__name__ # TODO: is this generally valid?
248+
func.__name__ = name.__name__ # TODO: is this generally valid?
250249
func.__doc__ = name.__doc__
251250
else:
252251
func.__name__ = name
253252
func.__doc__ = getattr(delegate, name).__doc__
254253
return func
255254

256-
257255
@staticmethod
258256
def delegate_names(delegate, accessors, typ, overwrite=False):
259257
"""
@@ -309,7 +307,6 @@ def add_delegate_accessors(cls):
309307
return add_delegate_accessors
310308

311309

312-
313310
wrap_delegate_names = Delegator.delegate_names
314311
# TODO: the `delegate` arg to `wrap_delegate_names` is really only relevant
315312
# for a docstring. It'd be nice if we didn't require it and could duck-type
@@ -346,6 +343,3 @@ def add_delegate_accessors(cls):
346343
# The third thing to consider moving into the general case is
347344
# core.strings.StringMethods._wrap_result, which handles a bunch of cases
348345
# for how to wrap delegated outputs.
349-
350-
351-

pandas/core/base.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,6 @@ def __setattr__(self, key, value):
162162
object.__setattr__(self, key, value)
163163

164164

165-
166-
167-
168-
169165
class GroupByError(Exception):
170166
pass
171167

pandas/core/categorical.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from pandas.core.common import is_null_slice
3030

3131
from pandas.core.algorithms import factorize, take_1d, unique1d
32-
from pandas.core.accessors import PandasDelegate
32+
from pandas.core import accessors
3333
from pandas.core.base import (PandasObject,
3434
NoNewAttributesMixin, _shared_docs)
3535
import pandas.core.common as com
@@ -2014,7 +2014,20 @@ def repeat(self, repeats, *args, **kwargs):
20142014
# The Series.cat accessor
20152015

20162016

2017-
class CategoricalAccessor(PandasDelegate, NoNewAttributesMixin):
2017+
@accessors.wrap_delegate_names(delegate=Categorical,
2018+
accessors=["rename_categories",
2019+
"reorder_categories",
2020+
"add_categories",
2021+
"remove_categories",
2022+
"remove_unused_categories",
2023+
"set_categories",
2024+
"as_ordered",
2025+
"as_unordered"],
2026+
typ="method")
2027+
@accessors.wrap_delegate_names(delegate=Categorical,
2028+
accessors=["categories", "ordered"],
2029+
typ="property")
2030+
class CategoricalAccessor(accessors.PandasDelegate, NoNewAttributesMixin):
20182031
"""
20192032
Accessor object for categorical properties of the Series values.
20202033
@@ -2037,6 +2050,13 @@ class CategoricalAccessor(PandasDelegate, NoNewAttributesMixin):
20372050
20382051
"""
20392052

2053+
@classmethod
2054+
def _make_accessor(cls, values):
2055+
if not is_categorical_dtype(values.dtype):
2056+
msg = "Can only use .cat accessor with a 'category' dtype"
2057+
raise AttributeError(msg)
2058+
return CategoricalAccessor(values.values, values.index)
2059+
20402060
def __init__(self, values, index):
20412061
self.categorical = values
20422062
self.index = index
@@ -2048,31 +2068,23 @@ def _delegate_property_get(self, name):
20482068
def _delegate_property_set(self, name, new_values):
20492069
return setattr(self.categorical, name, new_values)
20502070

2051-
@property
2052-
def codes(self):
2053-
from pandas import Series
2054-
return Series(self.categorical.codes, index=self.index)
2055-
20562071
def _delegate_method(self, name, *args, **kwargs):
20572072
from pandas import Series
20582073
method = getattr(self.categorical, name)
20592074
res = method(*args, **kwargs)
20602075
if res is not None:
20612076
return Series(res, index=self.index)
20622077

2078+
# TODO: Can we get this from _delegate_property_get?
2079+
# Would need to get self.index into the result
2080+
@property
2081+
def codes(self):
2082+
from pandas import Series
2083+
return Series(self.categorical.codes, index=self.index)
20632084

2064-
CategoricalAccessor._add_delegate_accessors(delegate=Categorical,
2065-
accessors=["categories",
2066-
"ordered"],
2067-
typ='property')
2068-
CategoricalAccessor._add_delegate_accessors(delegate=Categorical, accessors=[
2069-
"rename_categories", "reorder_categories", "add_categories",
2070-
"remove_categories", "remove_unused_categories", "set_categories",
2071-
"as_ordered", "as_unordered"], typ='method')
20722085

20732086
# utility routines
20742087

2075-
20762088
def _get_codes_for_values(values, categories):
20772089
"""
20782090
utility routine to turn values into codes given the specified categories

pandas/core/frame.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
from pandas.core.indexes.datetimes import DatetimeIndex
8989
from pandas.core.indexes.timedeltas import TimedeltaIndex
9090

91-
from pandas.core import accessors, base, nanops, ops
91+
from pandas.core import accessors, nanops, ops
9292
import pandas.core.common as com
9393

9494
import pandas.io.formats.format as fmt
@@ -6006,7 +6006,7 @@ def _put_str(s, space):
60066006
# ----------------------------------------------------------------------
60076007
# Add plotting methods to DataFrame
60086008
DataFrame.plot = accessors.AccessorProperty(gfx.FramePlotMethods,
6009-
gfx.FramePlotMethods)
6009+
gfx.FramePlotMethods)
60106010
DataFrame.hist = gfx.hist_frame
60116011

60126012

pandas/core/indexes/accessors.py

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
is_timedelta64_dtype, is_categorical_dtype,
1212
is_list_like)
1313

14-
from pandas.core.accessors import PandasDelegate
14+
from pandas.core import accessors
15+
1516
from pandas.core.base import NoNewAttributesMixin
1617
from pandas.core.indexes.datetimes import DatetimeIndex
1718
from pandas._libs.period import IncompatibleFrequency # noqa
@@ -85,7 +86,7 @@ def maybe_to_datetimelike(data, copy=False):
8586
"datetimelike index".format(type(data)))
8687

8788

88-
class Properties(PandasDelegate, NoNewAttributesMixin):
89+
class BaseDatetimeAccessor(accessors.PandasDelegate, NoNewAttributesMixin):
8990

9091
def __init__(self, values, index, name, orig=None):
9192
self.values = values
@@ -95,7 +96,7 @@ def __init__(self, values, index, name, orig=None):
9596
self._freeze()
9697

9798
def _delegate_property_get(self, name):
98-
from pandas import Series
99+
from pandas import Series, DataFrame
99100

100101
result = getattr(self.values, name)
101102

@@ -105,6 +106,9 @@ def _delegate_property_get(self, name):
105106
result = result.astype('int64')
106107
elif not is_list_like(result):
107108
return result
109+
elif isinstance(result, DataFrame):
110+
# e.g. TimedeltaProperties.components
111+
return result.set_index(self.index)
108112

109113
result = np.asarray(result)
110114

@@ -146,7 +150,18 @@ def _delegate_method(self, name, *args, **kwargs):
146150
return result
147151

148152

149-
class DatetimeProperties(Properties):
153+
# An alternative to decorating with @accessors.wrap_delegate_names
154+
# is to define each method individually, e.g.:
155+
# to_period = PandasDelegate._make_delegate_accessor(delegate=DatetimeIndex,
156+
# name='to_period',
157+
# typ='method')
158+
@accessors.wrap_delegate_names(delegate=DatetimeIndex,
159+
accessors=DatetimeIndex._datetimelike_ops,
160+
typ='property')
161+
@accessors.wrap_delegate_names(delegate=DatetimeIndex,
162+
accessors=DatetimeIndex._datetimelike_methods,
163+
typ='method')
164+
class DatetimeProperties(BaseDatetimeAccessor):
150165
"""
151166
Accessor object for datetimelike properties of the Series values.
152167
@@ -164,17 +179,13 @@ def to_pydatetime(self):
164179
return self.values.to_pydatetime()
165180

166181

167-
DatetimeProperties._add_delegate_accessors(
168-
delegate=DatetimeIndex,
169-
accessors=DatetimeIndex._datetimelike_ops,
170-
typ='property')
171-
DatetimeProperties._add_delegate_accessors(
172-
delegate=DatetimeIndex,
173-
accessors=DatetimeIndex._datetimelike_methods,
174-
typ='method')
175-
176-
177-
class TimedeltaProperties(Properties):
182+
@accessors.wrap_delegate_names(delegate=TimedeltaIndex,
183+
accessors=TimedeltaIndex._datetimelike_ops,
184+
typ='property')
185+
@accessors.wrap_delegate_names(delegate=TimedeltaIndex,
186+
accessors=TimedeltaIndex._datetimelike_methods,
187+
typ='method')
188+
class TimedeltaProperties(BaseDatetimeAccessor):
178189
"""
179190
Accessor object for datetimelike properties of the Series values.
180191
@@ -190,6 +201,7 @@ class TimedeltaProperties(Properties):
190201
def to_pytimedelta(self):
191202
return self.values.to_pytimedelta()
192203

204+
# TODO: Do this with wrap_delegate_names
193205
@property
194206
def components(self):
195207
"""
@@ -204,17 +216,13 @@ def components(self):
204216
return self.values.components.set_index(self.index)
205217

206218

207-
TimedeltaProperties._add_delegate_accessors(
208-
delegate=TimedeltaIndex,
209-
accessors=TimedeltaIndex._datetimelike_ops,
210-
typ='property')
211-
TimedeltaProperties._add_delegate_accessors(
212-
delegate=TimedeltaIndex,
213-
accessors=TimedeltaIndex._datetimelike_methods,
214-
typ='method')
215-
216-
217-
class PeriodProperties(Properties):
219+
@accessors.wrap_delegate_names(delegate=PeriodIndex,
220+
accessors=PeriodIndex._datetimelike_ops,
221+
typ='property')
222+
@accessors.wrap_delegate_names(delegate=PeriodIndex,
223+
accessors=PeriodIndex._datetimelike_methods,
224+
typ='method')
225+
class PeriodProperties(BaseDatetimeAccessor):
218226
"""
219227
Accessor object for datetimelike properties of the Series values.
220228
@@ -229,18 +237,20 @@ class PeriodProperties(Properties):
229237
"""
230238

231239

232-
PeriodProperties._add_delegate_accessors(
233-
delegate=PeriodIndex,
234-
accessors=PeriodIndex._datetimelike_ops,
235-
typ='property')
236-
PeriodProperties._add_delegate_accessors(
237-
delegate=PeriodIndex,
238-
accessors=PeriodIndex._datetimelike_methods,
239-
typ='method')
240-
241-
242240
class CombinedDatetimelikeProperties(DatetimeProperties, TimedeltaProperties):
243241
# This class is never instantiated, and exists solely for the benefit of
244242
# the Series.dt class property. For Series objects, .dt will always be one
245243
# of the more specific classes above.
246244
__doc__ = DatetimeProperties.__doc__
245+
246+
@classmethod
247+
def _make_accessor(cls, values):
248+
try:
249+
return maybe_to_datetimelike(values)
250+
except Exception:
251+
msg = "Can only use .dt accessor with datetimelike values"
252+
raise AttributeError(msg)
253+
254+
255+
DatetimeAccessor = CombinedDatetimelikeProperties
256+
# Alias to mirror CategoricalAccessor

0 commit comments

Comments
 (0)