Skip to content

DOC: Add examples for creating an index accessor #57867

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

Merged
merged 25 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e460550
DOC: Add examples for creating an index accessor
sjalkote Mar 16, 2024
87cc9dc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2024
15311db
Fix E501 lint errors
sjalkote Mar 16, 2024
2c084dc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2024
f527348
Fix failing CI builds for Docstring
sjalkote Mar 16, 2024
ed15adf
fix newline
sjalkote Mar 16, 2024
d07cea3
Merge branch 'main' into main
sjalkote Mar 17, 2024
7e45c99
Remove redundant code-block directives
sjalkote Mar 17, 2024
2c47a6f
Use variables for `Examples` content
sjalkote Mar 17, 2024
1420df5
Merge remote-tracking branch 'origin/main'
sjalkote Mar 17, 2024
64866c6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 17, 2024
152108b
Merge branch 'main' into main
sjalkote Mar 17, 2024
604bbb0
Fix docstring validation errors
sjalkote Mar 19, 2024
c6fee47
Fix docstring validation errors
sjalkote Mar 19, 2024
d0650be
Merge branch 'pandas-dev:main' into main
sjalkote Apr 1, 2024
3003ebb
Merge branch 'pandas-dev:main' into main
sjalkote Apr 1, 2024
8845750
Fix CI errors
sjalkote Apr 1, 2024
32dc509
Fix CI errors
sjalkote Apr 1, 2024
958428f
Fix CI errors
sjalkote Apr 1, 2024
2ef33aa
Fix CI errors
sjalkote Apr 1, 2024
a7e6e37
Fix CI errors (again lol)
sjalkote Apr 1, 2024
c6f848a
Merge branch 'main' into main
sjalkote Apr 2, 2024
b08a129
Merge branch 'main' into main
sjalkote Apr 2, 2024
67805c7
Undo validation ignore for docstrings
sjalkote Apr 2, 2024
6d525e7
Merge branch 'main' into main
sjalkote Apr 3, 2024
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
3 changes: 3 additions & 0 deletions ci/code_checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,9 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
-i "pandas.api.extensions.ExtensionArray.unique RT03,SA01" \
-i "pandas.api.extensions.ExtensionArray.view SA01" \
-i "pandas.api.extensions.register_extension_dtype SA01" \
-i "pandas.api.extensions.register_index_accessor" \
-i "pandas.api.extensions.register_series_accessor" \
-i "pandas.api.extensions.register_dataframe_accessor" \
-i "pandas.api.indexers.BaseIndexer PR01,SA01" \
-i "pandas.api.indexers.FixedForwardWindowIndexer PR01,SA01" \
-i "pandas.api.indexers.VariableOffsetWindowIndexer PR01,SA01" \
Expand Down
136 changes: 94 additions & 42 deletions pandas/core/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def __get__(self, obj, cls):
return accessor_obj


@doc(klass="", others="")
@doc(klass="", examples="", others="")
def _register_accessor(name: str, cls):
"""
Register a custom accessor on {klass} objects.
Expand All @@ -255,51 +255,26 @@ def _register_accessor(name: str, cls):

Notes
-----
When accessed, your accessor will be initialized with the pandas object
the user is interacting with. So the signature must be
This function allows you to register a custom-defined accessor class for {klass}.
The requirements for the accessor class are as follows:

.. code-block:: python
* Must contain an init method that:

def __init__(self, pandas_object): # noqa: E999
...
* accepts a single {klass} object

For consistency with pandas methods, you should raise an ``AttributeError``
if the data passed to your accessor has an incorrect dtype.
* raises an AttributeError if the {klass} object does not have correctly
matching inputs for the accessor

>>> pd.Series(["a", "b"]).dt
Traceback (most recent call last):
...
AttributeError: Can only use .dt accessor with datetimelike values
* Must contain a method for each access pattern.

Examples
--------
In your library code::

@pd.api.extensions.register_dataframe_accessor("geo")
class GeoAccessor:
def __init__(self, pandas_obj):
self._obj = pandas_obj

@property
def center(self):
# return the geographic center point of this DataFrame
lat = self._obj.latitude
lon = self._obj.longitude
return (float(lon.mean()), float(lat.mean()))
* The methods should be able to take any argument signature.

def plot(self):
# plot this array's data on a map, e.g., using Cartopy
pass
* Accessible using the @property decorator if no additional arguments are
needed.

Back in an interactive IPython session:

.. code-block:: ipython

In [1]: ds = pd.DataFrame({{"longitude": np.linspace(0, 10),
...: "latitude": np.linspace(0, 20)}})
In [2]: ds.geo.center
Out[2]: (5.0, 10.0)
In [3]: ds.geo.plot() # plots data on a map
Examples
--------
{examples}
"""

def decorator(accessor):
Expand All @@ -318,21 +293,98 @@ def decorator(accessor):
return decorator


@doc(_register_accessor, klass="DataFrame")
_register_df_examples = """
An accessor that only accepts integers could
have a class defined like this:

>>> @pd.api.extensions.register_dataframe_accessor("int_accessor")
... class IntAccessor:
... def __init__(self, pandas_obj):
... if not all(pandas_obj[col].dtype == 'int64' for col in pandas_obj.columns):
... raise AttributeError("All columns must contain integer values only")
... self._obj = pandas_obj
...
... def sum(self):
... return self._obj.sum()
...
>>> df = pd.DataFrame([[1, 2], ['x', 'y']])
>>> df.int_accessor
Traceback (most recent call last):
...
AttributeError: All columns must contain integer values only.
>>> df = pd.DataFrame([[1, 2], [3, 4]])
>>> df.int_accessor.sum()
0 4
1 6
dtype: int64"""


@doc(_register_accessor, klass="DataFrame", examples=_register_df_examples)
def register_dataframe_accessor(name: str):
from pandas import DataFrame

return _register_accessor(name, DataFrame)


@doc(_register_accessor, klass="Series")
_register_series_examples = """
An accessor that only accepts integers could
have a class defined like this:

>>> @pd.api.extensions.register_series_accessor("int_accessor")
... class IntAccessor:
... def __init__(self, pandas_obj):
... if not pandas_obj.dtype == 'int64':
... raise AttributeError("The series must contain integer data only")
... self._obj = pandas_obj
...
... def sum(self):
... return self._obj.sum()
...
>>> df = pd.Series([1, 2, 'x'])
>>> df.int_accessor
Traceback (most recent call last):
...
AttributeError: The series must contain integer data only.
>>> df = pd.Series([1, 2, 3])
>>> df.int_accessor.sum()
6"""


@doc(_register_accessor, klass="Series", examples=_register_series_examples)
def register_series_accessor(name: str):
from pandas import Series

return _register_accessor(name, Series)


@doc(_register_accessor, klass="Index")
_register_index_examples = """
An accessor that only accepts integers could
have a class defined like this:

>>> @pd.api.extensions.register_index_accessor("int_accessor")
... class IntAccessor:
... def __init__(self, pandas_obj):
... if not all(isinstance(x, int) for x in pandas_obj):
... raise AttributeError("The index must only be an integer value")
... self._obj = pandas_obj
...
... def even(self):
... return [x for x in self._obj if x % 2 == 0]
>>> df = pd.DataFrame.from_dict(
... {"row1": {"1": 1, "2": "a"}, "row2": {"1": 2, "2": "b"}}, orient="index"
... )
>>> df.index.int_accessor
Traceback (most recent call last):
...
AttributeError: The index must only be an integer value.
>>> df = pd.DataFrame(
... {"col1": [1, 2, 3, 4], "col2": ["a", "b", "c", "d"]}, index=[1, 2, 5, 8]
... )
>>> df.index.int_accessor.even()
[2, 8]"""


@doc(_register_accessor, klass="Index", examples=_register_index_examples)
def register_index_accessor(name: str):
from pandas import Index

Expand Down