13
13
)
14
14
import warnings
15
15
16
- from pandas .util ._decorators import doc
16
+ from pandas .util ._decorators import doc , Appender
17
17
from pandas .util ._exceptions import find_stack_level
18
18
19
19
@@ -255,51 +255,15 @@ def _register_accessor(name: str, cls):
255
255
256
256
Notes
257
257
-----
258
- When accessed, your accessor will be initialized with the pandas object
259
- the user is interacting with. So the signature must be
260
-
261
- .. code-block:: python
262
-
263
- def __init__(self, pandas_object): # noqa: E999
264
- ...
265
-
266
- For consistency with pandas methods, you should raise an ``AttributeError``
267
- if the data passed to your accessor has an incorrect dtype.
268
-
269
- >>> pd.Series(["a", "b"]).dt
270
- Traceback (most recent call last):
271
- ...
272
- AttributeError: Can only use .dt accessor with datetimelike values
273
-
274
- Examples
275
- --------
276
- In your library code::
277
-
278
- @pd.api.extensions.register_dataframe_accessor("geo")
279
- class GeoAccessor:
280
- def __init__(self, pandas_obj):
281
- self._obj = pandas_obj
282
-
283
- @property
284
- def center(self):
285
- # return the geographic center point of this DataFrame
286
- lat = self._obj.latitude
287
- lon = self._obj.longitude
288
- return (float(lon.mean()), float(lat.mean()))
289
-
290
- def plot(self):
291
- # plot this array's data on a map, e.g., using Cartopy
292
- pass
293
-
294
- Back in an interactive IPython session:
295
-
296
- .. code-block:: ipython
297
-
298
- In [1]: ds = pd.DataFrame({{"longitude": np.linspace(0, 10),
299
- ...: "latitude": np.linspace(0, 20)}})
300
- In [2]: ds.geo.center
301
- Out[2]: (5.0, 10.0)
302
- In [3]: ds.geo.plot() # plots data on a map
258
+ This function allows you to register a custom-defined accessor class for {klass}.
259
+ The requirements for the accessor class are as follows:
260
+
261
+ * Must contain an init method that:
262
+ * accepts a single {klass} object
263
+ * raises an {AttributeError} if the {klass} object does not have correctly matching inputs for the accessor
264
+ * Must contain a method for each access pattern.
265
+ * The methods should be able to take any argument signature.
266
+ * Accessible using the @property decorator if no additional arguments are needed.
303
267
"""
304
268
305
269
def decorator (accessor ):
@@ -318,20 +282,125 @@ def decorator(accessor):
318
282
return decorator
319
283
320
284
285
+ _register_df_doc = """
286
+ Examples
287
+ --------
288
+ In your library code, an accessor that only accepts integers could have a class defined like this:
289
+ .. code-block:: python
290
+
291
+ import pandas as pd
292
+
293
+ @pd.api.extensions.register_dataframe_accessor("int_accessor")
294
+ class IntAccessor:
295
+ def __init__(self, pandas_obj):
296
+ if not all(pandas_obj[col].dtype == 'int64' for col in pandas_obj.columns):
297
+ raise AttributeError("All columns must contain integer values only")
298
+ self._obj = pandas_obj
299
+
300
+ def sum(self):
301
+ return self._obj.sum()
302
+
303
+
304
+ Back in an interactive IPython session:
305
+ .. code-block:: ipython
306
+ >>> df = pd.DataFrame([[1, 2], ['x', 'y']])
307
+ >>> df.int_accessor
308
+ Traceback (most recent call last):
309
+ ...
310
+ AttributeError: All columns must contain integer values only. Did you mean: '_accessors'?
311
+ >>> df = pd.DataFrame([[1, 2], [3, 4]])
312
+ >>> df.int_accessor.sum()
313
+ 0 4
314
+ 1 6
315
+ dtype: int64
316
+ """
317
+
318
+
319
+ @Appender (_register_df_doc )
321
320
@doc (_register_accessor , klass = "DataFrame" )
322
321
def register_dataframe_accessor (name : str ):
323
322
from pandas import DataFrame
324
323
325
324
return _register_accessor (name , DataFrame )
326
325
327
326
327
+ _series_doc = """
328
+ Examples
329
+ --------
330
+ In your library code, an accessor that only accepts integers could have a class defined like this:
331
+ .. code-block:: python
332
+
333
+ import pandas as pd
334
+
335
+ @pd.api.extensions.register_series_accessor("int_accessor")
336
+ class IntAccessor:
337
+ def __init__(self, pandas_obj):
338
+ if not all(pandas_obj[col].dtype == 'int64' for col in pandas_obj.columns):
339
+ raise AttributeError("All columns must contain integer values only")
340
+ self._obj = pandas_obj
341
+
342
+ def sum(self):
343
+ return self._obj.sum()
344
+
345
+
346
+ Back in an interactive IPython session:
347
+ .. code-block:: ipython
348
+ >>> df = pd.Series([1, 2, 'x'])
349
+ >>> df.int_accessor
350
+ Traceback (most recent call last):
351
+ ...
352
+ AttributeError: The series must contain integer data only. Did you mean: '_accessors'?
353
+ >>> df = pd.Series([1, 2, 3])
354
+ >>> df.int_accessor.sum
355
+ 6
356
+ """
357
+
358
+
359
+ @Appender (_series_doc )
328
360
@doc (_register_accessor , klass = "Series" )
329
361
def register_series_accessor (name : str ):
330
362
from pandas import Series
331
363
332
364
return _register_accessor (name , Series )
333
365
334
366
367
+ _index_doc = """
368
+ Examples
369
+ --------
370
+ In your library code, an accessor that only accepts integers could have a class defined like this:
371
+ .. code-block:: python
372
+
373
+ import pandas as pd
374
+
375
+ @pd.api.extensions.register_index_accessor("int_accessor")
376
+ class IntAccessor:
377
+ def __init__(self, pandas_obj):
378
+ if not all(isinstance(x, int) for x in pandas_obj):
379
+ raise AttributeError("The index must only be an integer value")
380
+ self._obj = pandas_obj
381
+
382
+ def even(self):
383
+ return [x for x in self._obj if x % 2 == 0]
384
+
385
+ Back in an interactive IPython session:
386
+ .. code-block:: ipython
387
+ >>> df = pd.DataFrame.from_dict({
388
+ 'row1': {'1':1, '2':'a'},
389
+ 'row2': {'1':2, '2':'b'}
390
+ },orient='index')
391
+ >>> df.index.int_accessor
392
+ Traceback (most recent call last):
393
+ ...
394
+ AttributeError: The index must only be an integer value. Did you mean: '_accessors'?
395
+ >>> df = pd.DataFrame({
396
+ 'col1': [1, 2, 3, 4],
397
+ 'col2': ['a', 'b', 'c', 'd']
398
+ }, index=[1, 2, 5, 8])
399
+ >>> df.index.my_accessor.even()
400
+ [2,8]
401
+ """
402
+
403
+ @Appender (_index_doc )
335
404
@doc (_register_accessor , klass = "Index" )
336
405
def register_index_accessor (name : str ):
337
406
from pandas import Index
0 commit comments