8000 API: Added axis argument to rename, reindex by TomAugspurger · Pull Request #17800 · pandas-dev/pandas · GitHub
[go: up one dir, main page]

Skip to content

API: Added axis argument to rename, reindex #17800

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 2 commits into from
Oct 10, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
API: Accept 'axis' keyword argument for reindex
  • Loading branch information
TomAugspurger committed Oct 10, 2017
commit 0e668bfd35260d2f77e7230ebffafd476b92991a
24 changes: 22 additions & 2 deletions doc/source/basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,15 @@ following can be done:
This means that the reindexed Series's index is the same Python object as the
DataFrame's index.

.. versionadded:: 0.21.0

:meth:`DataFrame.reindex` also supports an "axis-style" calling convention,
where you specify a single ``labels`` argument and the ``axis`` it applies to.

.. ipython:: python
df.reindex(['c', 'f', 'b'], axis='index')
df.reindex(['three', 'two', 'one'], axis='columns')
.. seealso::

Expand Down Expand Up @@ -1413,12 +1422,23 @@ Series can also be used:

.. ipython:: python
df.rename(columns={'one' : 'foo', 'two' : 'bar'},
index={'a' : 'apple', 'b' : 'banana', 'd' : 'durian'})
df.rename(columns={'one': 'foo', 'two': 'bar'},
index={'a': 'apple', 'b': 'banana', 'd': 'durian'})
If the mapping doesn't include a column/index label, it isn't renamed. Also
extra labels in the mapping don't throw an error.

.. versionadded:: 0.21.0

:meth:`DataFrame.rename` also supports an "axis-style" calling convention, where
you specify a single ``mapper`` and the ``axis`` to apply that mapping to.

.. ipython:: python
df.rename({'one': 'foo', 'two': 'bar'}, axis='columns'})
df.rename({'a': 'apple', 'b': 'banana', 'd': 'durian'}, axis='columns'})
The :meth:`~DataFrame.rename` method also provides an ``inplace`` named
parameter that is by default ``False`` and copies the underlying data. Pass
``inplace=True`` to rename the data in place.
Expand Down
39 changes: 29 additions & 10 deletions doc/source/whatsnew/v0.21.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,39 @@ For example:
# the following is now equivalent
df.drop(columns=['B', 'C'])

``rename`` now also accepts axis keyword
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _whatsnew_0210.enhancements.rename_reindex_axis:

The :meth:`~DataFrame.rename` method has gained the ``axis`` keyword as an
alternative to specify the ``axis`` to target (:issue:`12392`).
``rename``, ``reindex`` now also accept axis keyword
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Copy link
Contributor

Choose a reason for hiding this comment

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

needs to be same length as the title (hard to tell)

.. ipython::
The :meth:`DataFrame.rename` and :meth:`DataFrame.reindex` methods have gained
the ``axis`` keyword to specify the axis to target with the operation
(:issue:`12392`).

Here's ``rename``:

.. ipython:: python

df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
df.rename(str.lower, axis='columns')
df.rename(id, axis='index')

And ``reindex``:

.. ipython:: python

df.reindex(['A', 'B', 'C'], axis='columns')
df.reindex([0, 1, 3], axis='index')

The "index, columns" style continues to work as before.

.. ipython:: python

df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
df.rename(str.lower, axis='columns')
df.rename(id, axis='index')
df.rename(index=id, columns=str.lower)
df.reindex(index=[0, 1, 3], columns=['A', 'B', 'C'])

Copy link
Member

Choose a reason for hiding this comment

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

maybe add df.reindex(index=[0, 1, 3], columns=['A', 'B', 'C']) as well? to make it clear that it is not only rename but also reindex that keeps the old way

The ``.rename(index=id, columns=str.lower)`` style continues to work as before.
We *highly* encourage using named arguments to avoid confusion.
We *highly* encourage using named arguments to avoid confusion when using either
style.

.. _whatsnew_0210.enhancements.categorical_dtype:

Expand Down
96 changes: 87 additions & 9 deletions pandas/core/frame.py
8000 < 8000 td class="blob-num blob-num-addition empty-cell">
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
_values_from_object,
_maybe_box_datetimelike,
_dict_compat,
_all_not_none,
standardize_mapping)
from pandas.core.generic import NDFrame, _shared_docs
from pandas.core.index import (Index, MultiIndex, _ensure_index,
Expand Down Expand Up @@ -112,10 +113,10 @@
by : str or list of str
Name or list of names which refer to the axis items.""",
versionadded_to_excel='',
optional_mapper="""mapper : dict-like or function
Applied to the axis specified by `axis`""",
optional_labels="""labels : array-like, optional
New labels / index to conform the axis specified by 'axis' to.""",
optional_axis="""axis : int or str, optional
Axis to target. Can be either the axis name ('rows', 'columns')
Axis to target. Can be either the axis name ('index', 'columns')
or number (0, 1).""",
)

Expand Down Expand Up @@ -2801,24 +2802,25 @@ def _validate_axis_style_args(self, arg, arg_name, index, columns,
elif axis == 'columns':
columns = arg

elif all(x is not None for x in (arg, index, columns)):
elif _all_not_none(arg, index, columns):
msg = (
"Cannot specify all of '{arg_name}', 'index', and 'columns'. "
"Specify either {arg_name} and 'axis', or 'index' and "
"'columns'."
).format(arg_name=arg_name)
raise TypeError(msg)

elif axis is None and (arg is not None and index is not None):
elif _all_not_none(arg, index):
# This is the "ambiguous" case, so emit a warning
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe worth factoring this function out if its common with the drop changes? not sure

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe put in pandas/util/_validators.py with all other arg validation code

msg = (
"Interpreting call to '.{method_name}(a, b)' as "
"'.{method_name}(index=a, columns=b)'. "
"Use keyword arguments to remove any ambiguity."
).format(method_name=method_name)
warnings.warn(msg)
warnings.warn(msg, stacklevel=3)
index, columns = arg, index
elif index is None and columns is None:
elif index is None:
# This is for the default axis, like reindex([0, 1])
index = arg
< 10000 details class="details-overlay details-reset position-relative d-inline-block"> Copy link
Member

Choose a reason for hiding this comment

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

This is for the default (labels, axis=0) case ? (if so, maybe good to add a comment)

return index, columns

Expand Down Expand Up @@ -2948,7 +2950,11 @@ def align(self, other, join='outer', axis=None, level=None, copy=True,
broadcast_axis=broadcast_axis)

@Appender(_shared_docs['reindex'] % _shared_doc_kwargs)
def reindex(self, index=None, columns=None, **kwargs):
def reindex(self, labels=None, index=None, columns=None, axis=None,
**kwargs):
index, columns = self._validate_axis_style_args(labels, 'labels',
index, columns,
axis, 'reindex')
return super(DataFrame, self).reindex(index=index, columns=columns,
**kwargs)

Expand All @@ -2960,9 +2966,81 @@ def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True,
method=method, level=level, copy=copy,
limit=limit, fill_value=fill_value)

@Appender(_shared_docs['rename'] % _shared_doc_kwargs)
def rename(self, mapper=None, index=None, columns=None, axis=None,
**kwargs):
"""Alter axes labels.

Function / dict values must be unique (1-to-1). Labels not contained in
a dict / Series will be left as-is. Extra labels listed don't throw an
error.

See the :ref:`user guide <basics.rename>` for more.

Parameters
----------
mapper, index, columns : dict-like or function, optional
dict-like or functions transformations to apply to
that axis' values. Use either ``mapper`` and ``axis`` to
specify the axis to target with ``mapper``, or ``index`` and
``columns``.
axis : int or str, optional
Axis to target with ``mapper``. Can be either the axis name
('index', 'columns') or number (0, 1). The default is 'index'.
copy : boolean, default True
Also copy underlying data
inplace : boolean, default False
Whether to return a new %(klass)s. If True then value of copy is
ignored.
level : int or level name, default None
In case of a MultiIndex, only rename labels in the specified
level.

Returns
-------
renamed : DataFrame

See Also
--------
pandas.DataFrame.rename_axis

Examples
--------

``DataFrame.rename`` supports two calling conventions

* ``(index=index_mapper, columns=columns_mapper, ...)
* ``(mapper, axis={'index', 'columns'}, ...)

We *highly* recommend using keyword arguments to clarify your
intent.

>>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
>>> df.rename(index=str, columns={"A": "a", "B": "c"})
a c
0 1 4
1 2 5
2 3 6

>>> df.rename(index=str, columns={"A": "a", "C": "c"})
a B
0 1 4
1 2 5
2 3 6

Using axis-style parameters

>>> df.rename(str.lower, axis='columns')
a b
0 1 4
1 2 5
2 3 6

>>> df.rename({1: 2, 2: 4}, axis='index')
A B
0 1 4
2 2 5
4 3 6
"""
index, columns = self._validate_axis_style_args(mapper, 'mapper',
index, columns,
axis, 'rename')
Expand Down
56 changes: 53 additions & 3 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ def swaplevel(self, i=-2, j=-1, axis=0):

Examples
--------

>>> s = pd.Series([1, 2, 3])
>>> s
0 1
Expand All @@ -790,16 +791,29 @@ def swaplevel(self, i=-2, j=-1, axis=0):
5 3
dtype: int64

Since ``DataFrame`` doesn't have a ``.name`` attribute,
only mapping-type arguments are allowed.

>>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
>>> df.rename(2)
Traceback (most recent call last):
...
TypeError: 'int' object is not callable

``DataFrame.rename`` supports two calling conventions

* ``(index=index_mapper, columns=columns_mapper, ...)
* ``(mapper, axis={'index', 'columns'}, ...)

We *highly* recommend using keyword arguments to clarify your
intent.

>>> df.rename(index=str, columns={"A": "a", "B": "c"})
a c
0 1 4
1 2 5
2 3 6

>>> df.rename(index=str, columns={"A": "a", "C": "c"})
a B
0 1 4
Expand All @@ -819,6 +833,8 @@ def swaplevel(self, i=-2, j=-1, axis=0):
0 1 4
2 2 5
4 3 6

See the :ref:`user guide <basics.rename>` for more.
"""

@Appender(_shared_docs['rename'] % dict(axes='axes keywords for this'
Expand Down Expand Up @@ -904,6 +920,7 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False):

Examples
--------

>>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
>>> df.rename_axis("foo") # scalar, alters df.index.name
A B
Expand Down Expand Up @@ -2764,10 +2781,11 @@ def sort_index(self, axis=0, level=None, ascending=True, inplace=False,

Parameters
----------
%(axes)s : array-like, optional (can be specified in order, or as
keywords)
%(optional_labels)s
%(axes)s : array-like, optional (should be specified using keywords)
New labels / index to conform to. Preferably an Index object to
avoid duplicating data
%(optional_axis)s
method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
method to use for filling holes in reindexed DataFrame.
Please note: this is only applicable to DataFrames/Series with a
Expand Down Expand Up @@ -2799,6 +2817,14 @@ def sort_index(self, axis=0, level=None, ascending=True, inplace=False,
Examples
--------

``DataFrame.reindex`` supports two calling conventions

* ``(index=index_labels, columns=column_labels, ...)
* ``(labels, axis={'index', 'columns'}, ...)

We *highly* recommend using keyword arguments to clarify your
intent.

Create a dataframe with some fictional data.

>>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
Expand Down Expand Up @@ -2849,6 +2875,26 @@ def sort_index(self, axis=0, level=None, ascending=True, inplace=False,
IE10 404 0.08
Chrome 200 0.02

We can also reindex the columns.
Copy link
Contributor

Choose a reason for hiding this comment

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

same add the warning?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added above.


>>> df.reindex(columns=['http_status', 'user_agent'])
http_status user_agent
Firefox 200 NaN
Chrome 200 NaN
Safari 404 NaN
IE10 404 NaN
Konqueror 301 NaN

Or we can use "axis-style" keyword arguments

>>> df.reindex(['http_status', 'user_agent'], axis="columns")
http_status user_agent
Firefox 200 NaN
Chrome 200 NaN
Safari 404 NaN
IE10 404 NaN
Konqueror 301 NaN

To further illustrate the filling functionality in
``reindex``, we will create a dataframe with a
monotonically increasing index (for example, a sequence
Expand Down Expand Up @@ -2911,6 +2957,8 @@ def sort_index(self, axis=0, level=None, ascending=True, inplace=False,
desired indexes. If you do want to fill in the ``NaN`` values present
in the original dataframe, use the ``fillna()`` method.

See the :ref:`user guide <basics.reindexing>` for more.

Returns
-------
reindexed : %(klass)s
Expand All @@ -2919,7 +2967,9 @@ def sort_index(self, axis=0, level=None, ascending=True, inplace=False,
# TODO: Decide if we care about having different examples for different
# kinds

@Appender(_shared_docs['reindex'] % dict(axes="axes", klass="NDFrame"))
@Appender(_shared_docs['reindex'] % dict(axes="axes", klass="NDFrame",
optional_labels="",
optional_axis=""))
def reindex(self, *args, **kwargs):

# construct the args
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
axes='items, major_axis, minor_axis',
klass="Panel",
axes_single_arg="{0, 1, 2, 'items', 'major_axis', 'minor_axis'}",
optional_mapper='', optional_axis='')
optional_mapper='', optional_axis='', optional_labels='')
_shared_doc_kwargs['args_transpose'] = ("three positional arguments: each one"
"of\n%s" %
_shared_doc_kwargs['axes_single_arg'])
Expand Down
Loading
0