Skip to content

Commit

Permalink
API: Deprecate renamae_axis and reindex_axis (pandas-dev#17842)
Browse files Browse the repository at this point in the history
* API: Deprecate renamae_axis and reindex_axis

Closes pandas-dev#17833

* REF: Refactor axis style validator to generic

This sets us up to re-use it for Panel reindex

* fixup! API: Deprecate renamae_axis and reindex_axis

* fixup! API: Deprecate renamae_axis and reindex_axis

* fixup! API: Deprecate renamae_axis and reindex_axis

* fixup! API: Deprecate renamae_axis and reindex_axis

* Ugh

* fixup! API: Deprecate renamae_axis and reindex_axis

* Fixup
  • Loading branch information
TomAugspurger authored and Krzysztof Chomski committed Oct 16, 2017
1 parent daaaae3 commit a4a11a1
Show file tree
Hide file tree
Showing 23 changed files with 199 additions and 106 deletions.
7 changes: 5 additions & 2 deletions doc/source/basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1201,8 +1201,11 @@ With a DataFrame, you can simultaneously reindex the index and columns:
df
df.reindex(index=['c', 'f', 'b'], columns=['three', 'two', 'one'])
For convenience, you may utilize the :meth:`~Series.reindex_axis` method, which
takes the labels and a keyword ``axis`` parameter.
You may also use ``reindex`` with an ``axis`` keyword:

.. ipython:: python
df.reindex(index=['c', 'f', 'b'], axis='index')
Note that the ``Index`` objects containing the actual axis labels can be
**shared** between objects. So if we have a Series and a DataFrame, the
Expand Down
3 changes: 3 additions & 0 deletions doc/source/whatsnew/v0.21.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,8 @@ Deprecations
- ``.get_value`` and ``.set_value`` on ``Series``, ``DataFrame``, ``Panel``, ``SparseSeries``, and ``SparseDataFrame`` are deprecated in favor of using ``.iat[]`` or ``.at[]`` accessors (:issue:`15269`)
- Passing a non-existent column in ``.to_excel(..., columns=)`` is deprecated and will raise a ``KeyError`` in the future (:issue:`17295`)
- ``raise_on_error`` parameter to :func:`Series.where`, :func:`Series.mask`, :func:`DataFrame.where`, :func:`DataFrame.mask` is deprecated, in favor of ``errors=`` (:issue:`14968`)
- Using :meth:`DataFrame.rename_axis` and :meth:`Series.rename_axis` to alter index or column *labels* is now deprecated in favor of using ``.rename``. ``rename_axis`` may still be used to alter the name of the index or columns (:issue:`17833`).
- :meth:`~DataFrame.reindex_axis` has been deprecated in favor of :meth:`~DataFrame.reindex`. See :ref`here` <whatsnew_0210.enhancements.rename_reindex_axis> for more (:issue:`17833`).

.. _whatsnew_0210.deprecations.select:

Expand Down Expand Up @@ -998,6 +1000,7 @@ Reshaping
- Bug in :func:`unique` where checking a tuple of strings raised a ``TypeError`` (:issue:`17108`)
- Bug in :func:`concat` where order of result index was unpredictable if it contained non-comparable elements (:issue:`17344`)
- Fixes regression when sorting by multiple columns on a ``datetime64`` dtype ``Series`` with ``NaT`` values (:issue:`16836`)
- Bug in :fun:`pivot_table` where the result's columns did not preserve the categorical dtype of ``columns`` when ``dropna`` was ``False`` (:issue:`17842`)

Numeric
^^^^^^^
Expand Down
8 changes: 2 additions & 6 deletions pandas/core/computation/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _align_core(terms):
for axis, items in zip(range(ndim), axes):
ti = terms[i].value

if hasattr(ti, 'reindex_axis'):
if hasattr(ti, 'reindex'):
transpose = isinstance(ti, pd.Series) and naxes > 1
reindexer = axes[naxes - 1] if transpose else items

Expand All @@ -104,11 +104,7 @@ def _align_core(terms):
).format(axis=axis, term=terms[i].name, ordm=ordm)
warnings.warn(w, category=PerformanceWarning, stacklevel=6)

if transpose:
f = partial(ti.reindex, index=reindexer, copy=False)
else:
f = partial(ti.reindex_axis, reindexer, axis=axis,
copy=False)
f = partial(ti.reindex, reindexer, axis=axis, copy=False)

terms[i].update(f())

Expand Down
64 changes: 11 additions & 53 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
_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 @@ -2736,7 +2735,7 @@ def reindexer(value):
if isinstance(loc, (slice, Series, np.ndarray, Index)):
cols = maybe_droplevels(self.columns[loc], key)
if len(cols) and not cols.equals(value.columns):
value = value.reindex_axis(cols, axis=1)
value = value.reindex(cols, axis=1)
# now align rows
value = reindexer(value).T

Expand Down Expand Up @@ -2783,47 +2782,6 @@ def reindexer(value):

return np.atleast_2d(np.asarray(value))

def _validate_axis_style_args(self, arg, arg_name, index, columns,
axis, method_name):
if axis is not None:
# Using "axis" style, along with a positional arg
# Both index and columns should be None then
axis = self._get_axis_name(axis)
if index is not None or columns is not None:
msg = (
"Can't specify both 'axis' and 'index' or 'columns'. "
"Specify either\n"
"\t.{method_name}.rename({arg_name}, axis=axis), or\n"
"\t.{method_name}.rename(index=index, columns=columns)"
).format(arg_name=arg_name, method_name=method_name)
raise TypeError(msg)
if axis == 'index':
index = arg
elif axis == 'columns':
columns = arg

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 _all_not_none(arg, index):
# This is the "ambiguous" case, so emit a warning
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, stacklevel=3)
index, columns = arg, index
elif index is None:
# This is for the default axis, like reindex([0, 1])
index = arg
return index, columns

@property
def _series(self):
result = {}
Expand Down Expand Up @@ -2952,11 +2910,11 @@ def align(self, other, join='outer', axis=None, level=None, copy=True,
@Appender(_shared_docs['reindex'] % _shared_doc_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)
axes = self._validate_axis_style_args(labels, 'labels',
axes=[index, columns],
axis=axis, method_name='reindex')
kwargs.update(axes)
return super(DataFrame, self).reindex(**kwargs)

@Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs)
def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True,
Expand Down Expand Up @@ -3041,11 +2999,11 @@ def rename(self, mapper=None, index=None, columns=None, axis=None,
2 2 5
4 3 6
"""
index, columns = self._validate_axis_style_args(mapper, 'mapper',
index, columns,
axis, 'rename')
return super(DataFrame, self).rename(index=index, columns=columns,
**kwargs)
axes = self._validate_axis_style_args(mapper, 'mapper',
axes=[index, columns],
axis=axis, method_name='rename')
kwargs.update(axes)
return super(DataFrame, self).rename(**kwargs)

@Appender(_shared_docs['fillna'] % _shared_doc_kwargs)
def fillna(self, value=None, method=None, axis=None, inplace=False,
Expand Down
92 changes: 74 additions & 18 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
from pandas.core.dtypes.missing import isna, notna
from pandas.core.dtypes.generic import ABCSeries, ABCPanel, ABCDataFrame

from pandas.core.common import (_values_from_object,
from pandas.core.common import (_all_not_none,
_values_from_object,
_maybe_box_datetimelike,
SettingWithCopyError, SettingWithCopyWarning,
AbstractMethodError)
Expand Down Expand Up @@ -729,6 +730,51 @@ def swaplevel(self, i=-2, j=-1, axis=0):
result._data.set_axis(axis, labels.swaplevel(i, j))
return result

def _validate_axis_style_args(self, arg, arg_name, axes,
axis, method_name):
out = {}
for i, value in enumerate(axes):
if value is not None:
out[self._AXIS_NAMES[i]] = value

aliases = ', '.join(self._AXIS_NAMES.values())
if axis is not None:
# Using "axis" style, along with a positional arg
# Both index and columns should be None then
axis = self._get_axis_name(axis)
if any(x is not None for x in axes):
msg = (
"Can't specify both 'axis' and {aliases}. "
"Specify either\n"
"\t.{method_name}({arg_name}, axis=axis), or\n"
"\t.{method_name}(index=index, columns=columns)"
).format(arg_name=arg_name, method_name=method_name,
aliases=aliases)
raise TypeError(msg)
out[axis] = arg

elif _all_not_none(arg, *axes):
msg = (
"Cannot specify all of '{arg_name}', {aliases}. "
"Specify either {arg_name} and 'axis', or {aliases}."
).format(arg_name=arg_name, aliases=aliases)
raise TypeError(msg)

elif _all_not_none(arg, axes[0]):
# This is the "ambiguous" case, so emit a warning
msg = (
"Interpreting call to '.{method_name}(a, b)' as "
"'.{method_name}(index=a, columns=b)'. " # TODO
"Use keyword arguments to remove any ambiguity."
).format(method_name=method_name)
warnings.warn(msg, stacklevel=3)
out[self._AXIS_ORDERS[0]] = arg
out[self._AXIS_ORDERS[1]] = axes[0]
elif axes[0] is None:
# This is for the default axis, like reindex([0, 1])
out[self._AXIS_ORDERS[0]] = arg
return out

# ----------------------------------------------------------------------
# Rename

Expand Down Expand Up @@ -893,17 +939,12 @@ def f(x):
rename.__doc__ = _shared_docs['rename']

def rename_axis(self, mapper, axis=0, copy=True, inplace=False):
"""
Alter index and / or columns using input function or functions.
A scalar or list-like for ``mapper`` will alter the ``Index.name``
or ``MultiIndex.names`` attribute.
A function or dict for ``mapper`` will alter the labels.
Function / dict values must be unique (1-to-1). Labels not contained in
a dict / Series will be left as-is.
"""Alter the name of the index or columns.
Parameters
----------
mapper : scalar, list-like, dict-like or function, optional
mapper : scalar, list-like, optional
Value to set the axis name attribute.
axis : int or string, default 0
copy : boolean, default True
Also copy underlying data
Expand All @@ -913,38 +954,45 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False):
-------
renamed : type of caller or None if inplace=True
Notes
-----
Prior to version 0.21.0, ``rename_axis`` could also be used to change
the axis *labels* by passing a mapping or scalar. This behavior is
deprecated and will be removed in a future version. Use ``rename``
instead.
See Also
--------
pandas.NDFrame.rename
pandas.Series.rename, pandas.DataFrame.rename
pandas.Index.rename
Examples
--------
>>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
>>> df.rename_axis("foo") # scalar, alters df.index.name
>>> df.rename_axis("foo")
A B
foo
0 1 4
1 2 5
2 3 6
>>> df.rename_axis(lambda x: 2 * x) # function: alters labels
A B
0 1 4
2 2 5
4 3 6
>>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns") # mapping
ehh B
>>> df.rename_axis("bar", axis="columns")
bar A B
0 1 4
1 2 5
2 3 6
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
non_mapper = is_scalar(mapper) or (is_list_like(mapper) and not
is_dict_like(mapper))
if non_mapper:
return self._set_axis_name(mapper, axis=axis, inplace=inplace)
else:
msg = ("Using 'rename_axis' to alter labels is deprecated. "
"Use '.rename' instead")
warnings.warn(msg, FutureWarning, stacklevel=2)
axis = self._get_axis_name(axis)
d = {'copy': copy, 'inplace': inplace}
d[axis] = mapper
Expand Down Expand Up @@ -2981,6 +3029,11 @@ def reindex(self, *args, **kwargs):
tolerance = kwargs.pop('tolerance', None)
fill_value = kwargs.pop('fill_value', np.nan)

# Series.reindex doesn't use / need the axis kwarg
# We pop and ignore it here, to make writing Series/Frame generic code
# easier
kwargs.pop("axis", None)

if kwargs:
raise TypeError('reindex() got an unexpected keyword '
'argument "{0}"'.format(list(kwargs.keys())[0]))
Expand Down Expand Up @@ -3085,11 +3138,14 @@ def _reindex_multi(self, axes, copy, fill_value):
@Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs)
def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True,
limit=None, fill_value=np.nan):
msg = ("'.reindex_axis' is deprecated and will be removed in a future "
"version. Use '.reindex' instead.")
self._consolidate_inplace()

axis_name = self._get_axis_name(axis)
axis_values = self._get_axis(axis_name)
method = missing.clean_reindex_fill_method(method)
warnings.warn(msg, FutureWarning, stacklevel=3)
new_index, indexer = axis_values.reindex(labels, method, level,
limit=limit)
return self._reindex_with_indexers({axis: [new_index, indexer]},
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -901,7 +901,7 @@ def reset_identity(values):
result.index.get_indexer_for(ax.values))
result = result.take(indexer, axis=self.axis)
else:
result = result.reindex_axis(ax, axis=self.axis)
result = result.reindex(ax, axis=self.axis)

elif self.group_keys:

Expand Down
4 changes: 2 additions & 2 deletions pandas/core/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ def _setitem_with_indexer(self, indexer, value):
# so the object is the same
index = self.obj._get_axis(i)
labels = index.insert(len(index), key)
self.obj._data = self.obj.reindex_axis(labels, i)._data
self.obj._data = self.obj.reindex(labels, axis=i)._data
self.obj._maybe_update_cacher(clear=True)
self.obj.is_copy = None

Expand Down Expand Up @@ -1132,7 +1132,7 @@ def _getitem_iterable(self, key, axis=None):
if labels.is_unique and Index(keyarr).is_unique:

try:
return self.obj.reindex_axis(keyarr, axis=axis)
return self.obj.reindex(keyarr, axis=axis)
except AttributeError:

# Series
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -3283,8 +3283,8 @@ def apply(self, f, axes=None, filter=None, do_integrity_check=False,

for k, obj in aligned_args.items():
axis = getattr(obj, '_info_axis_number', 0)
kwargs[k] = obj.reindex_axis(b_items, axis=axis,
copy=align_copy)
kwargs[k] = obj.reindex(b_items, axis=axis,
copy=align_copy)

kwargs['mgr'] = self
applied = getattr(b, f)(**kwargs)
Expand Down
14 changes: 11 additions & 3 deletions pandas/core/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1197,13 +1197,21 @@ def _wrap_result(self, result, axis):
return self._construct_return_type(result, axes)

@Appender(_shared_docs['reindex'] % _shared_doc_kwargs)
def reindex(self, items=None, major_axis=None, minor_axis=None, **kwargs):
def reindex(self, labels=None,
items=None, major_axis=None, minor_axis=None,
axis=None, **kwargs):
major_axis = (major_axis if major_axis is not None else
kwargs.pop('major', None))
minor_axis = (minor_axis if minor_axis is not None else
kwargs.pop('minor', None))
return super(Panel, self).reindex(items=items, major_axis=major_axis,
minor_axis=minor_axis, **kwargs)
axes = self._validate_axis_style_args(
labels, 'labels', axes=[items, major_axis, minor_axis],
axis=axis, method_name='reindex')
if self.ndim >= 4:
# Hack for PanelND
axes = {}
kwargs.update(axes)
return super(Panel, self).reindex(**kwargs)

@Appender(_shared_docs['rename'] % _shared_doc_kwargs)
def rename(self, items=None, major_axis=None, minor_axis=None, **kwargs):
Expand Down
14 changes: 14 additions & 0 deletions pandas/core/panel4d.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,18 @@ def panel4d_init(self, data=None, labels=None, items=None, major_axis=None,
dtype=dtype)


def panel4d_reindex(self, labs=None, labels=None, items=None, major_axis=None,
minor_axis=None, axis=None, **kwargs):
# Hack for reindex_axis deprecation
# Ha, we used labels for two different things
# I think this will work still.
axes = self._validate_axis_style_args(
labs, 'labels',
axes=[labels, items, major_axis, minor_axis],
axis=axis, method_name='reindex')
kwargs.update(axes)
return super(Panel, self).reindex(**kwargs)


Panel4D.__init__ = panel4d_init
Panel4D.reindex = panel4d_reindex
Loading

0 comments on commit a4a11a1

Please sign in to comment.